I have the following problem:
I have a subroutine on Fortran 77, which must return a one-dimensional array, D_Y. All values which are needed are transferred correctly from the main program, but three elements of the matrix D_Y (D_Y(16-18)) are set to 0.0 and I don't know why.
NDIM=18, P1 and P2 are right, all parameters, which they have in equation are right too, but their sum is always set to 0!
SUBROUTINE DY(Y,T,PARAM,NDIM,D_Y)
IMPLICIT NONE
real(8),intent(in)::Y(*)
real(8),intent(in)::PARAM(*),T
real(8),intent(out)::D_Y(NDIM)
integer::D
integer,intent(in)::NDIM
real(8)::R,R1,R2,P1,P2
open(18,file='kinetic2.txt',status='unknown')
R=((Y(1)-Y(4))**2.D0+(Y(2)-Y(5))**2.D0+(Y(3)-Y(6))**2.D0)**0.5D0
R1=((Y(1)-Y(13))**2.D0+(Y(2)-Y(14))**2.D0+(Y(3)-Y(15))**2.D0)**0.5D0
R2=((Y(4)-Y(13))**2.D0+(Y(5)-Y(14))**2.D0+(Y(6)-Y(15))**2.D0)**0.5D0
DO D=1,6
D_Y(D)=Y(D+6)
END DO
DO D=7,9
D_Y(D)=-(PARAM(1)*PARAM(3))*(Y(D-6)-Y(D-3))/((R)**3.D0)
END DO
DO D=10,12
D_Y(D)=-(PARAM(1)*PARAM(2))*(Y(D-6)-Y(D-9))/((R)**3.D0)
END DO
DO D=13,15
D_Y(D)=Y(D+3)
END DO
DO D=16,18
P1=-(PARAM(1)*PARAM(3))*(Y(D-3)-Y(D-12))/((R2)**3.D0)
P2=-(PARAM(1)*PARAM(2))*(Y(D-3)-Y(D-15))/((R1)**3.D0)
D_Y(D)=P1+P2
write(18,*) P1,P2,D_Y(D)
END DO
RETURN
END
If your compiler is treating your code as fixed form a D in column 1, such as in the line
D_Y(D)=P1+P2
may be understood to be a comment. Check your compiler documentation and options.
Related
A similar question was answered in Fortran runtime warning: temporary array. However, the solutions do not quite help me in my case.
Inside a subroutine, I have a subroutine call as:
subroutine initialize_prim(prim)
real(kind=wp), dimension(2, -4:204), intent(out) :: prim
call double_gaussian(prim(1, :))
end subroutine initialize_prim
subroutine double_gaussian(y)
real(kind=wp), dimension(-4:204), intent(out) :: y
integer :: i
do i = -4, 204
y(i) = 0.5 * ( &
exp(-((r(i) - r0))**2) + exp(-((r(i) + r0)/std_dev)**2))
end do
end subroutine double_gaussian
This gives an error message saying that fortran creates a temporary array for "y" in "double_gaussian". Having read a bit about continguous arrays, I understand why this error appears.
Now, looking at my whole program, it would be very tedious to invert the order of the arrays for "prim", so that solution is not really possible.
For creating assumed-shapes in "double_gaussian", I tried doing,
real(kind=wp), dimension(:), intent(out) :: y
integer :: i
do i = -4, 204
y(i) = 0.5 * ( &
exp(-((r(i) - r0))**2) + exp(-((r(i) + r0)/std_dev)**2))
end do
end subroutine double_gaussian
This, however, causes fortran to crash with the error message
"Index '-4' of dimension 1 of array 'y' below lower bound of 1".
It seems that for the assumed-shape format, the indexing is nonetheless assumed to start with 1, whereas it starts at -4 as in my case.
Is there a way to resolve this issue?
I think that you have perhaps misinterpreted a compiler warning as an error. Usually compilers issue a warning when they create temporary arrays - it's a useful aid to high-performance programming. But I'm not sure a compiler ever regards that as an error. And yes, I understand why you might not want to re-order your array just to avoid that
As for the crash - you have discovered that Fortran routines don't automagically know about the lower bounds of arrays which you have carefully set to be other than 1 (nor their upper bounds either). If it is necessary you have to pass the bounds (usually only the lower bound, the routine can figure out the upper bound itself) in the argument list.
However, it rarely is necessary, and it doesn't seem to be in your code - the loop to set each value of the y array could (if I understand correctly) be replaced by
y = 0.5 * (exp(-((r - r0))**2) + exp(-((r + r0)/std_dev)**2))
PS I think that this part of your question, about routines not respecting other-than-1 array lower bounds, is almost certainly a duplicate of several others asked hereabouts but which I couldn't immediately find.
I need cells index numbers, which fulfil following conditions:
Q(i)<=5 and V(i)/=1
(size(Q)==size(V)). I wrote something like this:
program test
implicit none
integer, allocatable, dimension(:):: R
integer Q(5)
integer V(5)
integer counter,limit,i
counter=0
limit=5
V=(/0,0,1,0,0/)
Q=(/5,10,2,7,2/)
do i=1,5
if((Q(i)<=5).AND.(V(i)/=1)) then
counter=counter+1
end if
end do
allocate(R(counter))
counter=1
do i=1,5
if((Q(i)<=5).AND.(V(i)/=1)) then
R(counter)=i
counter=counter+1
end if
end do
deallocate(R)
end program test
but I don't think it is a very efficient . Is there any better solution for this problem?
I can remove one loop by writing
program test
implicit none
integer, allocatable, dimension(:):: R
integer Q(5)
integer V(5)
integer counter,limit,i
counter=0
limit=5
V=(/0,0,1,0,0/)
Q=(/5,10,2,7,2/)
V=-V+1
allocate(R((count(V*Q<=5)-count(V*Q==0))))
counter=1
do i=1,size(Q)
if((Q(i)<=5).AND.(V(i)==1)) then
R(counter)=i
counter=counter+1
end if
end do
end program test
The question is very close to being a duplicate but explaining why would be a cumbersome comment.
Answers to that question take advantage of a common idiom:
PACK((/(i,i=1,SIZE(mask))/), mask)
This returns an array of 1-based indexes corresponding to .TRUE. elements of the logical array mask. For that question mask was the result of arr.gt.min but mask can be any rank-1 logical array.
Here, mask could well be Q.le.5.and.V.ne.1 (noting Q and V are the same length`).
In Fortran 95 (which is why I'm using (/../) and .ne.) one doesn't have access to the modern feature of automatic array allocation, so a manual allocation will be required. Something like
logical mask(5)
mask = Q.le.5.and.V.ne.1
ALLOCATE(R(COUNT(mask))
R = PACK((/(i,i=1,5)/),mask)
As an incentive to use a modern compiler, with Fortran 2003 compliance enabled, this is the same as
R = PACK((/(i,i=1,5)/), Q.le.5.and.V.ne.1)
(with appropriate other declarations, etc.)
When considering doing this creation in a subroutine it is exceptionally important to think about array bounds if using non-1-based indexing or subarrays. See my answer in the linked question for details.
The code below , when I execute it, ub,u, p, q arrays are shown like 101 times since ii=101 in the code. What I mean is the loop does not work. Could you look at it please
ii=101;
dt=0.0001;
t = 0:dt:1000;
dx=0.01; %step size
pi=4.*atan(1);
fik=0.4;
H1=0.5;
H1D=0;
A=0;
AD=0.1;
ADinit=AD;
c1b=0.5;
c2b=1-c1b;
dc=0.001;
for i=1:ii;
x=(i-1)*dx;
fikness=fik*sin(pi*x);
ub1(i)=(c1b-H1D*(x-0.5)+AD/2*(x-0.5)^2)/(H1-0.5*fikness-A*(x-0.5))
ub2(i)=(c2b+H1D*(x-0.5)-AD/2*(x-0.5)^2)/(1-H1+0.5*fikness+A*(x-0.5))
end
c1=c1b+dc;
c2=1-c1;
for i=1:ii;
x=(i-1)*dx;
fikness=fik*sin(pi*x);
u1(i)=(c1-H1D*(x-0.5)+AD/2*(x-0.5)^2)./(H1-0.5*fikness-A*(x-0.5))
u2(i)=(c2+H1D*(x-0.5)-AD/2*(x-0.5)^2)./(1-H1+0.5*fikness+A*(x-0.5))
end
p1(1)=0.5*(1-u1(1)^2);
q1(1)=0;
p2(1)=0.5*(1-u2(1)^2);
q2(1)=0;
for i=2:ii
q1(i)=q1(i-1)-dx*(u1(i-1)-ub1(i-1))/dt
p1(i)=0.5*(1-u1(i)^2)+q1(i)
end
for i=2:ii;
q2(i)=q2(i-1)-dx*(u2(i-1)-ub2(i-1))/dt
p2(i)=0.5*(1-u2(i)^2)+q2(i)
end
ub1,ub2,u1,u2,p1,p2,q1,q2 =1*101, all of them have one row 101 column How can I access the whole arrays of ub1,ub2,u1,u2,p1,p2,q1,q2 ??
the command prompt will show all calculations which are not followed by a semicolon. even if you are assigning one element of an array, it will show the whole array.
p(1)=1
this shows all of p, not just the first element. put semicolon at the end of each assignment statement or calculation like
p2(i)=0.5*(1-u2(i)^2)+q2(i);
then at the end of the code write
u1
u2
this should show the final vectors
I am new to Fortran, and I would like to be able to write a two-dimensional array to a text file, in a row-wise manner (spaces between columns, and each row on its own line). I have tried the following, and it seems to work in the following simple example:
PROGRAM test3
IMPLICIT NONE
INTEGER :: i, j, k, numrows, numcols
INTEGER, DIMENSION(:,:), ALLOCATABLE :: a
numrows=5001
numcols=762
ALLOCATE(a(numrows,numcols))
k=1
DO i=1,SIZE(a,1)
DO j=1,SIZE(a,2)
a(i,j)=k
k=k+1
END DO
END DO
OPEN(UNIT=12, FILE="aoutput.txt", ACTION="write", STATUS="replace")
DO i=1,numrows
WRITE(12,*) (a(i,j), j=1,numcols)
END DO
END PROGRAM test3
As I said, this seems to work fine in this simple example: the resulting text file, aoutput.txt, contains the numbers 1-762 on line 1, numbers 763-1524 on line 2, and so on.
But, when I use the above ideas (i.e., the last fifth-to-last, fourth-to-last, third-to-last, and second-to-last lines of code above) in a more complicated program, I run into trouble; each row is delimited (by a new line) only intermittently, it seems. (I have not posted, and probably will not post, here my entire complicated program/script--because it is rather long.) The lack of consistent row delimiters in my complicated program/script probably suggests another bug in my code, not with the four-line write-to-file routine above, since the above simple example appears to work okay. Still, I am wondering, can you please help me think if there is a better row-wise write-to-text file routine that I should be using?
Thank you very much for your time. I really appreciate it.
There's a few issues here.
The fundamental one is that you shouldn't use text as a data format for sizable chunks of data. It's big and it's slow. Text output is good for something you're going to read yourself; you aren't going to sit down with a printout of 3.81 million integers and flip through them. As the code below demonstrates, the correct text output is about 10x slower, and 50% bigger, than the binary output. If you move to floating point values, there are precision loss issues with using ascii strings as a data interchange format. etc.
If your aim is to interchange data with matlab, it's fairly easy to write the data into a format matlab can read; you can use the matOpen/matPutVariable API from matlab, or just write it out as an HDF5 array that matlab can read. Or you can just write out the array in raw Fortran binary as below and have matlab read it.
If you must use ascii to write out huge arrays (which, as mentioned, is a bad and slow idea) then you're running into problems with default record lengths in list-drected IO. Best is to generate at runtime a format string which correctly describes your output, and safest on top of this for such large (~5000 character wide!) lines is to set the record length explicitly to something larger than what you'll be printing out so that the fortran IO library doesn't helpfully break up the lines for you.
In the code below,
WRITE(rowfmt,'(A,I4,A)') '(',numcols,'(1X,I6))'
generates the string rowfmt which in this case would be (762(1X,I6)) which is the format you'll use for printing out, and the RECL option to OPEN sets the record length to be something bigger than 7*numcols + 1.
PROGRAM test3
IMPLICIT NONE
INTEGER :: i, j, k, numrows, numcols
INTEGER, DIMENSION(:,:), ALLOCATABLE :: a
CHARACTER(LEN=30) :: rowfmt
INTEGER :: txtclock, binclock
REAL :: txttime, bintime
numrows=5001
numcols=762
ALLOCATE(a(numrows,numcols))
k=1
DO i=1,SIZE(a,1)
DO j=1,SIZE(a,2)
a(i,j)=k
k=k+1
END DO
END DO
CALL tick(txtclock)
WRITE(rowfmt,'(A,I4,A)') '(',numcols,'(1X,I6))'
OPEN(UNIT=12, FILE="aoutput.txt", ACTION="write", STATUS="replace", &
RECL=(7*numcols+10))
DO i=1,numrows
WRITE(12,FMT=rowfmt) (a(i,j), j=1,numcols)
END DO
CLOSE(UNIT=12)
txttime = tock(txtclock)
CALL tick(binclock)
OPEN(UNIT=13, FILE="boutput.dat", ACTION="write", STATUS="replace", &
FORM="unformatted")
WRITE(13) a
CLOSE(UNIT=13)
bintime = tock(binclock)
PRINT *, 'ASCII time = ', txttime
PRINT *, 'Binary time = ', bintime
CONTAINS
SUBROUTINE tick(t)
INTEGER, INTENT(OUT) :: t
CALL system_clock(t)
END SUBROUTINE tick
! returns time in seconds from now to time described by t
REAL FUNCTION tock(t)
INTEGER, INTENT(IN) :: t
INTEGER :: now, clock_rate
call system_clock(now,clock_rate)
tock = real(now - t)/real(clock_rate)
END FUNCTION tock
END PROGRAM test3
This may be a very roundabout and time-consuming way of doing it, but anyway... You could simply print each array element separately, using advance='no' (to suppress insertion of a newline character after what was being printed) in your write statement. Once you're done with a line you use a 'normal' write statement to get the newline character, and start again on the next line. Here's a small example:
program testing
implicit none
integer :: i, j, k
k = 1
do i=1,4
do j=1,10
write(*, '(I2,X)', advance='no') k
k = k + 1
end do
write(*, *) '' ! this gives you the line break
end do
end program testing
When you run this program the output is as follows:
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
Using an "*" is list-directed IO -- Fortran will make the decisions for you. Some behaviors aren't specified. You could gain more control using a format statement. If you wanted to positively identify row boundaries you write a marker symbol after each row. Something like:
DO i=1,numrows
WRITE(12,*) a(i,:)
write (12, '("X")' )
END DO
Addendum several hours later:
Perhaps with large values of numcols the lines are too long for some programs that are you using to examine the file? For the output statement, try:
WRITE(12, '( 10(2X, I11) )' ) a(i,:)
which will break each row of the matrix, if it has more than 10 columns, into multiple, shorter lines in the file.
I am trying to take my array of numbers based on a variable that determines its size and sort it.
The array is created using the random numbers seed on Fortran 95. However when I try to sort it I run into big trouble. It compiles fine, but the array is printed with a lot of asterisks in it.
In addition I wanted to print my array sideways, (for instance something like this: 1 2 3 4 etc.) but I even failed at doing that. I realize that it must be done using the Advance="no" within a DO loop, but apparently that is erroneous as well.
Below is the code that I am using. If anyone is willing to let me know where I may be wrong I would be very grateful. Thanks for your time.
SUBROUTINE Sorter(num, numinteger)
INTEGER, INTENT(OUT):: num(100)
INTEGER, INTENT(IN):: numinteger
DO i=1, (numinteger-1)
min=num(i)
pos=i
DO j=i,numinteger
IF (num(j)<min)THEN
min=num(j)
pos=j
END IF
END DO
temp=num(i)
num(i)=min
num(pos)=temp
END DO
PRINT*, " "
PRINT*, "Sorted Numbers"
DO i=1, numinteger
WRITE(*,23,ADVANCE="NO") num
23 FORMAT (I2)
END DO
END SUBROUTINE
Thanks!
You don't have any spaces between your numbers, but you are also looping over the array, but not incrementing an index... you are asking the computer to print the entire array on each interation.
I think this should be: WRITE(*,23,ADVANCE="NO") num(i)