I am trying to write a code in Fortran 90 that reads the data from 3 different files (called "data1.dat", "data2.dat", data3.dat"). All the files are made by 3 columns and N lines (the lines depend on another code), and then do the following operation:
C(i)=(data1(i)-data2(i))/(data2(i)-data3(i))
When i represents the data "i" of the second column of each file.
I know how to read the files but I don't know how to manipulate the data from each file.
Any thoughts?
I will count on you to have all variable declaration done and all files open.
I don't have a compiler at hand so please verify my syntax before using it. Hope the following serves as something between a working code and a pseudo code:
OPEN (400, file='XXXX.YYY', status='UNKNOWN')
DO i = 1, NMAX
READ(100, *) data1
READ(200, *) data2
READ(300, *) data3
denom = data2 - data3
IF (denom .EQ. 0.) STOP "Cannot Divide by zero"
CCC = (data1-data2)/denom
WRITE (400, *) CCC
ENDDO
Each time through only one line in each file is read to memory; CCC is in turn calculated and written to an output file. In the next iteration, the program will read the line below instead of starting from top. No array needed.
In the end remember to close your files. You can play with IO formatting wherever you see fit.
Hope this helps.
Related
I have a data file with 84480 lines, I split them into 20 different files in a subroutine each having 4224 lines. Now I want to use these files one by one in another subroutine and do some analysis. But when I tried, I'm getting the runtime error: end of file.
Here is the structure of the main program
real (kind = 8) :: x(84480),y(84480),x1(4424),y1(4424)
open(1000,file='datafile.txt',status='old')
n = 20 ! number of configurations
m = 84480 ! total number of lines in all configurations
p = 4224 ! number of lines in a single configuration
no = 100 ! starting file number configurations
do i=1,m
read(1000,*) x(i),y(i)
end do
call split(x,y,m,n)
do i = 1,20
open(no)
do j = 1,p
read(no,*) x1(j),y1(j) ! error is occurring in here
end do
no = no + 1
end do
end
Here is the subroutine
subroutine split(x,y,m,n)
integer , intent (in) :: m,n
real (kind = 8) , intent(in) :: x(m),y(m)
integer :: i,k,j,p
p = 100
do i=0,n-1
k = i*4224
do j = k+1,k+4224
write(p,*) x(j),y(j)
end do
p = p + 1
end do
end subroutine split
This subroutine is producing output files fort.100 to fort.119 correctly. But it shows the following error
unit = 100, file = 'fort.100'
Fortran runtime error: End of file
Where am I going wrong?.
Of interest here is file connection. The program here uses two forms of connection: preconnection and the open statement. We ignore the connection to datafile.txt here.
We see preconnection in the subroutine with
write(p,*) x(j),y(j)
where the unit number p hasn't previously been in an open statement. This is where the default filename fort.100 (etc.) comes about.
After the subroutine has been called those 20 preconnected units have each had data written. Each of those connections is positioned at the end of the file. This is the notable part.
When, after the subroutine, we come to the loop with
open(no)
we are, because we haven't closed the connection, opening a connection with a unit number which is already connected to a file. This is perfectly acceptable. But we have to understand what this means.
The statement open(no) has no file specifier which means that the unit remains connected to the file it was connected to previously. As there is no other specifier given, nothing about the connection is changed. In particular, the connection is not repositioned: we are still at the end of each file.
So, come the read, we are attempting to read from the file when we are positioned at its end. Result: an end of file error.
Now, how to solve this?
One way, is to reposition the connection. Although we may want to open(no, position='rewind') we can't do that. There is, however
rewind no ! An unfortunate unit number name; could also be rewind(no).
Alternatively, as suggested in the comments on the question, we could close each connection, and reopen in the loop (with an explicit position='rewind') for the reading.
I have this Fortran code that reads two columns of data from an external file:
PRINT*, ' Q1 Q2 '
DO 2 J = 1, NPTS
READ(20,*) Q1(J),Q2(J)
WRITE(*,98) Q1(J),Q2(J)
So the file 20 is used, but it doesn't seem to like any names I use in the file, so the question is, what should I name the file as? It's a .txt by the way.
You do not read from files, but from units. So if you want to read from unit 20 as in your example, you need open a file using that unit first. This is typically done with an open statement, as suggested in the comments. For a file called yourfile.txt, your program would then read
OPEN( unit=20, file='yourfile.txt', status='old', &
action='read', iostat=stat )
if(stat /= 0) stop 'Could not open file'
PRINT*, ' Q1 Q2 '
DO 2 J = 1, NPTS
READ(20,*) Q1(J),Q2(J)
WRITE(*,98) Q1(J),Q2(J)
! ...
If you don't open a file first, at least gfortran and ifort try to read from a file named fort.<unit>, in your case fort.20. I couldn't find a reference in the Standard for that behavior, though.
Don't forget to close the file after reading, and try to avoid fixed unit numbers. If your compiler supports it, use the newunit= specifier when opening files.
My program works with a set of files (several millions). All the files were created earlier with some other code. Some of the files are empty, some have values; all of them have 'OLD' status. My program has to
open one of the files;
add some value to the END of THE FILE if the file contains numbers already or just put a first value if the file is empty;
close the file and go to another file processing.
Right now, if the file is non-empty, the program erase the file's previous content and just write a new value. I think, in order TO ADD a value to the end of existing non-empty file I need to use some clause in OPEN or WRITE statement in addition to the 'OLD' status. Which ones? Thank you.
It would be easier with a MWE, but nonetheless, what you could do is something like that, using the append keyword
open(unit=file_unit, file=filename, status='old', access='append')
You could try it on this simple example adapted from the Fortran Wikibook to see how it works
program write
implicit none
integer :: i, j
integer, parameter :: out_unit=10
print*,"Enter two integers:"
read (*,*) i, j
open (unit=out_unit, file="results.txt", action="write", status="old", access="append")
write (out_unit,*) "The product of",i," and",j
write (out_unit,*) "is",i*j
close (out_unit)
end program write
I have a shell script from which I pass a binary file to a fortran program such that
Mth=$1
loop=1
it=1
while test $it -le 12
do
Mth=`expr $Mth + $loop`
file="DataFile"$Mth".bin"
./fort_exe ${Yr} ${nt} ${it}
# Increment loop
it=`expr $it + 1`
done
This script is used to pass 12 files within a do loop to the fortran program. In the fortran program, I read the binary file passed from the shell script and I am trying to write a 2nd file which would compile in a single file all the data that was read from the consecutive files e.g.
!Open binary file passed from shell script
open(1,file='Datafile'//TRIM{Mth)//.bin',action='read',form='unformatted',access='direct', &
recl=4*x*y, status='old')
! Open write file for t 1. The status is different in t 1 and t > 1 so I open it twice: I guess there is a more elegant way to do this...
open(2,file='Newfile.bin',action='write',form='unformatted', &
access='stream', position='append', status='replace')
irec = 0
do t = 1, nt
! Read input file
irec = irec + 1
read(1,rec=irec) val(:,:)
! write output file
irecW= irec + (imonth-1)*nt
if ( t .eq. 1) write(2,pos=irecW) val(:,:)
! Close file after t = 1, update the status to old and reopen.
if ( t .eq. 2) then
close (2)
open(2,file='Newfile.bin',action='write',form='unformatted', &
access='stream', position='append',status='old')
endif
if ( t .ge. 2) write(2,pos=irecW) val(:,:)
enddo
I can read the binary data from the first file no problem but when I try and read from another program the binary data from the file that I wrote in the first program such that
open(1,file='Newfile.bin',action='read',form='unformatted', &
access='stream', status='old')
irec=0
do t = 1, nt
! Read input file
irec = irec + 1
read(1,pos=irec) val(:,:)
write(*,*) val(:,:)
enddo
val(:,:) is nothing but a list of zeros. This is the first time I use access=stream which I believe is the only way I can use position='append'. I have tried compiling with gfortran and ifort but I do not get any error messages.
Does anyone have any idea why this is happening?
Firstly, I do not think you need to close and reopen your output file as you are doing. The status specifier is only relevant to the open statement in which it appears: replace will delete Newfile.bin if it exists at that time, before opening a new file with the same name. The status is implicitly changed to old, but this does not affect any operations done to the file.
However, since your Fortran code does not know you run it 12 times, you should have a way of making sure the file is only replaced the first time and opened as old afterwards; otherwise, Newfile.bin will only contain the information from the last file processed.
As for reading in the wrong values, this most likely occurs because of the difference between direct access (where you can choose a record length) and stream access (where you cannot). With stream access, data is stored as a sequence of "file storage units". Their size is in general compiler-dependent, but is available through the module iso_fortran_env as file_storage_size; it is usually 8 bits. This means that each entry will usually occupy multiple storage units, so you have to take care that a read or write with the pos = specifier does not access the wrong storage units.
Edit:
Some example code writing and reading with stream access:
program stream
use, intrinsic :: iso_fortran_env
implicit none
integer :: i, offset
real(real32), dimension(4,6) :: val, nval
open(unit=2, file='Newfile.bin', action='readwrite', form='unformatted', &
access='stream', status='replace')
do i = 1,2
call random_number(val)
write(2) val
enddo
! The file now contains two sequences of 24 reals, each element of which
! occupies the following number of storage units:
offset = storage_size(val) / file_storage_size
! Retrieve the second sequence and compare:
read(2, pos = 1 + offset*size(val)) nval
print*, all(nval == val)
close(2)
end program
The value true should be printed to the screen.
Note also that it's not strictly necessary to specify a pos while writing your data to the file, because the file will automatically be positioned beyond the last record read or written.
That said, direct or stream access is most beneficial if you need to access the data in a non-sequential manner. If you only need to combine input files into one, it could be easier to write the output file with sequential access, for which you can also specify recl and position = 'append'.
You can check for the existence of a file in standard Fortran, by using the inquire statement:
logical :: exist
inquire(file="test.dat", exist=exist)
if (exist) then
print *, "File test.dat exists"
else
print *, "File test.dat does not exist"
end if
Alternatively you can have a look at the modFileSys library which provides libc like file manipulation routines.
As for appending and streams: Appending files is also possible when you use "classical" record based fortran files, you do not have to use streams for that.
i want to create new file at each loop, i don't know how to do...
!file1, file2,....., file[n]
OPEN (1,FILE='file1.out',ACCESS='SEQUENTIAL',STATUS='UNKNOWN')
do ph=1,N6
do i=1,nx-1
A(i)=mu*U(i-1)
end do
do j=0,nx
U(j)=A(j)
end do
if (mod(ph,Ne)==0) then ! ?
WRITE(1,200) nt,U(i)
endif
200 format(5E12.4)
end do
Or maybe, i can write with a newline or column ?
I'm a beginner in fortran.
Thanks
Your existing code should output more than just last write statement to the file ... it is a sequential file, which means that the output is added to the file in sequential order. If you are only seeing one output, perhaps that is all that the IF statement is causing to be output?
If you still wish to output to multiple files, the easiest way to output to multiple files is to reuse the unit number and have the program create filenames. You need to close the file / unit and reopen it. This is much more easier than have multiple open statements and unit numbers, which would quickly become awkward as the number of files increased. Here is a code fragment that assumes fewer than 100 files:
do i=1, N
write (filename, '("myfile", I2.2, ".txt")' ) I
open (file=filename,unit=16,...)
calculations...
write (16,'(5E12.4)') nt,U(i)
close (16)
end do