`Error: Syntax error in OPEN statement` when opening a file for reading - fortran

rogram readfromfile
implicit none
integer :: N, i
integer, dimension(130,2) :: cs
OPEN (UNIT=20,FILE='readtry.txt',STATUS='OLD',FORM='UNFORMATTED',)
do i=1,130
read (*,*) cs(i,1), cs(i,2)
enddo
do i=1,130
print *, cs(i,1), cs(i,2)
enddo
I am a beginner in programming, I just want read data from a file which has two columns and approximately 130 lines. I have tried to write down this code but its not working can someone please help?
The following error appears
gfortran -Wall -c "Rwarray.f95" (in directory: D:\Fortrandir\2Darrays)
Rwarray.f95:7:67:
OPEN (UNIT=20,FILE='readtry.txt',STATUS='OLD',FORM='UNFORMATTED',)
1
Error: Syntax error in OPEN statement at (1)
Compilation failed.

you have a compile time error, not a problem reading. But here's the gist of it:
It complains about a syntax error. Your statement is like this:
open(xxx, xxx, xxx, xxx,)
In order for it to compile, you need to remove the last comma. But I don't think that will give you what you want.
When you open the file, you declare it to be unformatted. Unformatted basically means that it contains the values in some form of binary. What's more, unformatted is not guaranteed to work between computers. So unless this file was written on your system, by a Fortran Program, with the FORM="UNFORMATTED" parameter, I don't think you'll get what you want.
I suspect that your input file looks something like this:
1 3
2 10
31 4711
That would be FORMATTED, not UNFORMATTED.
Then you use read(*, *). But the first * in there refers to "standard input", if you want to read from the file, you want to use the read(20, *), as 20 is the unit on which you opened the input file.
For the write statement, the * is correct, assuming that you want to write to "standard output" -- i.e. the screen.
What I'd further recommend is to use the error handling routines. Add these two variables to your declaration block:
integer :: ios
character(len=100) :: iomsg
And then use them whenever you open, read, or write:
open(unit=xx, file=xxx, status=xxx, action=xxx, form=xxx, io_stat=ios, iomsg=iomsg)
call check(ios, iomsg, "OPEN")
read(20, *, io_stat=ios, iomsg=iomsg) cs(1, i), cs(2, i)
call check(ios, iomsg, "READ")
You'd have to include the check subroutine, of course:
program readfromfile
implicit none
<declaraction block>
<execution block>
contains
subroutine check(ios, iomsg, action)
integer, intent(in) :: ios
character(len=*), intent(in) :: iomsg
character(len=*), intent(in), optional :: action
if (ios == 0) return ! No error occured, return
print*, "Error found. Error code:", ios
print*, "Message: ", trim(iomsg)
if (present(action)) print*, "Action was: ", trim(action)
stop 1
end subroutine check
end program readfromfile

Related

How to handle an optional group in a Fortran Namelist

I am working with a code originally written in Fortran 77 that makes use of namelists (supported by compiler extension at the time of its writing - this feature only became standard in Fortran 90) for reading input files. The namelist input files have groups of namelist variables in between (multiple) plain text headers and footers (see example.nml). Some groups of namelist variables are only read if certain conditions are met for previously read variables.
When reading all the namelist groups in a file in sequence, executables compiled with gfortran, ifort and nagfor all behave the same and give the expected output. However, when a given namelist group in the input file is to be skipped (the optional reading), gfortran and ifort executables handle this as desired, while the executable compiled with nagfor raises a runtime error:
Runtime Error: reader.f90, line 27: Expected NAMELIST group /GRP3/ but found /GRP2/
Program terminated by I/O error on unit 15 (File="example.nml",Formatted,Sequential)
As a minimal working example reproducing the problem, consider the namelist file example.nml and driver program reader.f90 given below, in which NUM2 from namelist group GRP2 should only be read if NUM1 from namelist group GRP1 equals 1:
example.nml:
this is a header
&GRP1 NUM1=1 /
&GRP2 NUM2=2 /
&GRP3 NUM3=3 /
this is a footer
reader.f90:
program reader
implicit none
character(len=40) :: hdr, ftr
integer :: num1, num2, num3, icode
! namelist definition
namelist/grp1/num1
namelist/grp2/num2
namelist/grp3/num3
! open input file
open(unit=15, file='example.nml', form='formatted', status='old', iostat=icode)
! read input data from namelists
read(15, '(a)') hdr
print *, hdr
read(15, grp1)
print *, num1
if (num1 == 1) then
read(15, grp2)
print *, num2
end if
read(15,grp3)
print *, num3
read(15, '(a)') ftr
print *, ftr
! close input file
close(unit=15)
end program reader
All executables give this expected output when NUM1=1:
this is a header
1
2
3
this is a footer
However, when e.g. NUM1=0, the executables compiled with gfortran and ifort give the desired output:
this is a header
0
3
this is a footer
while the executable compiled with nagfor (which is known for being strictly standard conforming), reads the header and first namelist group:
this is a header
0
but then terminates with the previously mentioned runtime error.
As indicated by the error message, example.nml is accessed sequentially, and if that is the case, /GRP2/ is the next record to be read, not /GRP3/ as the program logic asks for, so the error message makes sense to me.
So my question is this:
Can the shown behaviour be attributed to standard (non-)conformance enforced by nagfor and not gfortran and ifort?
If so, does this mean that the non-sequential reading observed with gfortran and ifort is due to extensions supported by these compilers (and not nagfor)? Can this be turned on/off using compiler flags?
The simplest workaround I can think of (minimal change to a large existing program), would be to add a dummy read(15,*) in an else branch for the if statement in reader.f90. This seems to work with all the mentioned compilers. Would this make the code standard conforming (Fortran 90 or later)?
These are the compiler versions and options that were used to compile the executables:
GNU Fortran (Ubuntu 9.1.0-2ubuntu2~18.04) 9.1.0:
gfortran -Wall -Wextra -fcheck=all -g -Og -fbacktrace reader.f90
Intel(R) Visual Fortran, Version 16.0 Build 20160415:
ifort -Od -debug:all -check:all -traceback reader.f90
NAG Fortran Compiler Release 6.1(Tozai) Build 6116:
nagfor -O0 -g -C reader.f90
When namelist formatting is requested on an external file, the namelist record is taken to commence at the record at the current position of the file.
The structure of a namelist input record is well defined by the language specification (see Fortran 2018 13.11.3.1, for example). In particular, this does not allow a mismatching namelist group name. nagfor complaining about this does so legitimately.
Several compilers do indeed appear to continue skipping over records until the namelist group is identified in a record, but I'm not aware of compiler flags available to control that behaviour. Historically, the case was generally that multiple namelists would be specified using distinct files.
Coming to your "simple workaround": this is, alas, not sufficient in the general case. Namelist input may consume several records of an external file. read(15,*) will advance the file position by only a single record. You will want to advance to after the terminating record for the namelist.
When you know the namelist is just that single record then the workaround is good.
#francescalus' answer and comment on that answer, clearly explained the first two parts of my question, while pointing out a flaw in the third part. In the hope that it may be useful to others that stumble upon a similar problem with a legacy code, here is the workaround I ended up implementing:
In essence, the solution is to ensure that the file record marker is always positioned correctly before attempting any namelist group read. This positioning is done in a subroutine that rewinds an input file, reads through records until it finds one with a matching group name (if not found, an error/warning can be raised) and then rewinds and repositions the file record marker to be ready for a namelist read.
subroutine position_at_nml_group(iunit, nml_group, status)
integer, intent(in) :: iunit
character(len=*), intent(in) :: nml_group
integer, intent(out) :: status
character(len=40) :: file_str
character(len=:), allocatable :: test_str
integer :: i, n
! rewind file
rewind(iunit)
! define test string, i.e. namelist group we're looking for
test_str = '&' // trim(adjustl(nml_group))
! search for the record containing the namelist group we're looking for
n = 0
do
read(iunit, '(a)', iostat=status) file_str
if (status /= 0) then
exit ! e.g. end of file
else
if (index(adjustl(file_str), test_str) == 1) then
! backspace(iunit) ?
exit ! i.e. found record we're looking for
end if
end if
n = n + 1 ! increment record counter
end do
! can possibly replace this section with "backspace(iunit)" after a
! successful string compare, but not sure that's legal for namelist records
! thus, the following:
if (status == 0) then
rewind(iunit)
do i = 1, n
read(iunit, '(a)')
end do
end if
end subroutine position_at_nml_group
Now, before reading any (possibly optional) namelist group, the file is positioned correctly first:
program new_reader
implicit none
character(len=40) :: line
integer :: num1, num2, num3, icode
! namelist definitions
namelist/grp1/num1
namelist/grp2/num2
namelist/grp3/num3
! open input file
open(unit=15, file='example.nml', access='sequential', &
form='formatted', status='old', iostat=icode)
read(15, '(a)') line
print *, line
call position_at_nml_group(15, 'GRP1', icode)
if (icode == 0) then
read(15, grp1)
print *, num1
end if
if (num1 == 1) then
call position_at_nml_group(15, 'GRP2', icode)
if (icode == 0) then
read(15, grp2)
print *, num2
end if
end if
call position_at_nml_group(15, 'GRP3', icode)
if (icode == 0) then
read(15, grp3)
print *, num3
end if
read(15, '(a)') line
print *, line
! close input file
close(unit=15)
contains
include 'position_at_nml_group.f90'
end program new_reader
Using this approach eliminates uncertainty in how different compilers treat not finding matching namelist groups at the current record in a file, generating the desired output for all compilers tested (nagfor, gfortran, ifort).
Note: For brevity, only a bare minimum of error checking is done in the code snippet shown here, this (and case insensitive string comparison!) should probably be added.

Fortran can't read txt files created by another program

When i try to read a txt file with the following code example:
double precision inp(100)
open(1,file='whatever.txt')
do i=1,100
read(1,*) inp(i)
enddo
close(1)
The program just ends when it arrives at the read sentence. I tried alternative ways to write the loop, like
do
read(1,*) inp
enddo
but it's the same. The funny part is that if i write a txt with fortran or by hand and then i try to read it, it works!!
i'm desperate, please, help me.
Here are a few ideas:
Take a unit number of 10 or larger, 1 might be taken by something like default i/o
Use iostat and iomsg to find out why the read fails:
integer :: iostat
character(len=100) :: iomsg
read(unit=u, fmt=*, iostat=iostat, iomsg=iomsg) inp(i)
if (iostat /= 0) then
print *, "Error reading inp"
print *, "i was ", i
print *, "Error was: ", trim(iomsg)
end if

Command line arguments in fortran (a filename, an integer, and another filename) [duplicate]

GCC version 4.6
The Problem: To find a way to feed in parameters to the executable, say a.out, from the command line - more specifically feed in an array of double precision numbers.
Attempt: Using the READ(*,*) command, which is older in the standard:
Program test.f -
PROGRAM MAIN
REAL(8) :: A,B
READ(*,*) A,B
PRINT*, A+B, COMMAND_ARGUMENT_COUNT()
END PROGRAM MAIN
The execution -
$ gfortran test.f
$ ./a.out 3.D0 1.D0
This did not work. On a bit of soul-searching, found that
$./a.out
3.d0,1.d0
4.0000000000000000 0
does work, but the second line is an input prompt, and the objective of getting this done in one-line is not achieved. Also the COMMAND_ARGUMENT_COUNT() shows that the numbers fed into the input prompt don't really count as 'command line arguments', unlike PERL.
If you want to get the arguments fed to your program on the command line, use the (since Fortran 2003) standard intrinsic subroutine GET_COMMAND_ARGUMENT. Something like this might work
PROGRAM MAIN
REAL(8) :: A,B
integer :: num_args, ix
character(len=12), dimension(:), allocatable :: args
num_args = command_argument_count()
allocate(args(num_args)) ! I've omitted checking the return status of the allocation
do ix = 1, num_args
call get_command_argument(ix,args(ix))
! now parse the argument as you wish
end do
PRINT*, A+B, COMMAND_ARGUMENT_COUNT()
END PROGRAM MAIN
Note:
The second argument to the subroutine get_command_argument is a character variable which you'll have to parse to turn into a real (or whatever). Note also that I've allowed only 12 characters in each element of the args array, you may want to fiddle around with that.
As you've already figured out read isn't used for reading command line arguments in Fortran programs.
Since you want to read an array of real numbers, you might be better off using the approach you've already figured out, that is reading them from the terminal after the program has started, it's up to you.
The easiest way is to use a library. There is FLAP or f90getopt available. Both are open source and licensed under free licenses.
The latter is written by Mark Gates and me, just one module and can be learned in minutes but contains all what is needed to parse GNU- and POSIX-like command-line options. The first is more sophisticated and can be used even in closed-source projects. Check them out.
Furthermore libraries at https://fortranwiki.org/fortran/show/Command-line+arguments
What READ (*,*) does is that it reads from the standard input. For example, the characters entered using the keyboard.
As the question shows COMMAND_ARGUMENT_COUNT() can be used to get the number of the command line arguments.
The accepted answer by High Performance Mark show how to retrieve the individual command line arguments separated by blanks as individual character strings using GET_COMMAND_ARGUMENT(). One can also get the whole command line using GET_COMMAND(). One then has to somehow parse that character-based information into the data in your program.
I very simple cases you just need the program requires, for example, two numbers, so you read one number from arg 1 and another form arg 2. That is simple. Or you can read a triplet of numbers from a single argument if they are comma-separated like 1,2,3 using a simple read(arg,*) nums(1:3).
For general complicated command line parsing one uses libraries such as those mentioned in the answer by Hani. You have set them up so that the library knows the expected syntax of the command line arguments and the data it should fill with the values.
There is a middle ground, that is still relatively simple, but one already have multiple arguments, that correspond to Fortran variables in the program, that may or may not be present. In that case one can use the namelist for the syntax and for the parsing.
Here is an example, the man point is the namelist /cmd/ name, point, flag:
implicit none
real :: point(3)
logical :: flag
character(256) :: name
character(1024) :: command_line
call read_command_line
call parse_command_line
print *, point
print *, "'",trim(name),"'"
print *, flag
contains
subroutine read_command_line
integer :: exenamelength
integer :: io, io2
command_line = ""
call get_command(command = command_line,status = io)
if (io==0) then
call get_command_argument(0,length = exenamelength,status = io2)
if (io2==0) then
command_line = "&cmd "//adjustl(trim(command_line(exenamelength+1:)))//" /"
else
command_line = "&cmd "//adjustl(trim(command_line))//" /"
end if
else
write(*,*) io,"Error getting command line."
end if
end subroutine
subroutine parse_command_line
character(256) :: msg
namelist /cmd/ name, point, flag
integer :: io
if (len_trim(command_line)>0) then
msg = ''
read(command_line,nml = cmd,iostat = io,iomsg = msg)
if (io/=0) then
error stop "Error parsing the command line or cmd.conf " // msg
end if
end if
end subroutine
end
Usage in bash:
> ./command flag=T name=\"data.txt\" point=1.0,2.0,3.0
1.00000000 2.00000000 3.00000000
'data.txt'
T
or
> ./command flag=T name='"data.txt"' point=1.0,2.0,3.0
1.00000000 2.00000000 3.00000000
'data.txt'
T
Escaping the quotes for the string is unfortunately necessary, because bash eats the first quotes.

How do I read in a consecutive row of a text file in each step of a do-loop in fortran?

I have a dataset of parameter values for 30 species, and I want to run a script that conducts a simulation for each species. The parameter values are currently stored in a .txt file, where each row is a different species, and each column is a different parameter value. What I'd like to do is set up a do-loop that reads in the relevant row of parameter values for each species, runs the simulation script, and writes a .txt file of the output for each species. Unfortunately, I'm new to fortran and having a lot of trouble understanding how to read in consecutive rows from a .txt file in each step of a do loop. I tried making a simplified script to test whether the read step was working:
PROGRAM DRIVER
IMPLICIT NONE
INTEGER :: mm ! I forgot this line in the first version of this question
and edited to add it in
CHARACTER(7) :: species !! the first column is the species name
REAL*8 :: leaf_variable ! The next 3 columns are variable values
REAL*8 :: stem_variable !
REAL*8 :: root_variable !
OPEN (12, file = "species_parameters.txt") ! open the .txt file
DO mm = 1,30 ! set up the do loop
READ (12,*) species, leaf_variable, stem_variable, root_variable
! Read in the species-specific parameter values
WRITE (*,*) species, leaf_variable, stem_variable, root_variable
! Print the values on the screen just to show the do loop runs
ENDDO
END PROGRAM DRIVER
But when I go to compile, I get the error:
At line XX of file XX (unit = 12, file = 'species_parameters.txt')
Fortran runtime error: End of file
What am I misunderstanding about opening and reading in this file?
Thanks very much for any help.
EDIT: I think I've narrowed down my problem. My understanding is that read() takes in one row in a .txt file at a time, so that in this example:
read(7, *) species, leaf_variable, stem_variable, root_variable
read(7, *) species, leaf_variable, stem_variable, root_variable
The variables should equal the values in the second row of the .txt file. Instead, no matter how many times I put in the read() function, the variable values equal the first row. And, even though there are only 4 columns, I can define as many variables as I want with a read() function:
read(7, *) species, leaf_variable, stem_variable, root_variable,
fake_variable1, fake_variable2, fake_variable3, fake_variable4
where the fake_variable values equal the values in the second row of the .txt file. Am I confused about what read() does, or is there something I need to do to keep my script from reading my entire .txt file as one line?
EDIT #2: The do loop reads line by line correctly now that I've saved my .txt file with Unix encoding using TextWrangler. The original file was saved as a .txt file with Excel. This seems to have solved it, but if anyone has suggestions for a better way to specify the input file format, I'd appreciate it. The first few lines of the input file look like this:
species1,1.2,6.54,10.9
species2,1.42,3.5,8.23
species3,0.85,2.41,4.9
A run time error is when you have an executable, execute it, and it crashes. A compile time error is when the compiler fails to produce an executable.
This code shouldn't compile, because you have IMPLICIT NONE, but haven't declared the integer mm.
What I'd recommend is to get more information:
program driver
use iso_fortran_env
implicit none
character(len=7) :: species
real(kind=real64) :: leaf_variable, stem_variable, root_variable
integer :: u, ioerr
character(len=120) :: iomsg
open(newunit=u, file='species_parameters.txt', action='read', status='old', iostat=ioerr, iomsg=iomsg)
if (ioerr /= 0) then
print *, "Error opening file"
print *, trim(iomsg)
stop 1
end if
do
read(u, *, iostat=ioerr, iomsg=iomsg) species, leaf_variable, stem_variable, root_variable
if (ioerr /= 0) exit ! exits the loop
write(*, *) species, leaf_variable, stem_variable, root_variable
end do
print *, trim(iomsg)
close(u)
end program driver
This will always print the "read past end of file" error, but this is just to check how to program reads anyway.
This should compile, and when you run it, it should give you some information on what is going wrong.

Error in fortran, undefined reference to subroutine

I am writing a subroutine and main function to call it, but getting error as undefined reference to ___. I found one reason: When I save the main and subroutine in the same file, compile and run that file, everything runs perfectly. However, when I save them into different .f90 files and try to run the main file, I get error. Is there any way I can make subroutine into a separate file and call into main calling program?
I got confused with another place - in the main program at !------ERROR------ place. I referred to Automatic width integer descriptor in fortran 90 I can use I0 as automatic width display indicator. But when I used the same, there is run time error expected integer but got character. Any idea about this?
! saved as sub_program.f90 file
SUBROUTINE sub_program (v1,v2,ctr)
IMPLICIT NONE
INTEGER, INTENT(IN) :: ctr
INTEGER, INTENT (OUT) :: v1,v2
SELECT CASE (ctr)
CASE (1)
v1=1
v2=0
CASE (2)
v1=0
v2=1
END SELECT
RETURN
END SUBROUTINE
! main calling program, saved as caller.f90
PROGRAM caller
IMPLICIT NONE
INTEGER :: v1,v2,ctr
DO ctr = 1,2,1
CALL sub_program (v1,v2,ctr)
WRITE (*,100) 'STEP = ',ctr,'V1 = ',v1,'V2 = ',v2 !------ERROR------
100 FORMAT (I0)
END DO
END PROGRAM
Thanks!
What is your compile command? For me, this compiles and runs normally
gfortran caller.f90 foo.f90 && ./a.out
I0 is an integer indicator, but some items following your WRITE statement are character strings. You can try, for example,
100 FORMAT (3(A, I0, 1X))
where 1X refers to a space.
As a note, if formatting is not terribly important and you're only interested in seeing some quick results, you can use the free format output (WRITE(*,*) ...).
EDIT: I had incorrectly referred to FORMAT as obsolete.