Struggling to write assert for Fortran - fortran

I am trying to write assert command for a Fortran program, but the whole thing is driving me nuts. Allow me to show you what have up to now:
File: Assert.h:
#define Assert(X) call Handle_Assert(.not.(X), #X, __FILE__, __LINE__)
File: Check_Assert.F90:
#include "Assert.h"
program Check_Assert
use Assert_Mod
Assert(1>2)
end program
And finally the file: Assert_Mod.F90:
module Assert_Mod
contains
subroutine Handle_Assert(fail, text, file, line)
implicit none
logical :: fail
character(*) :: text
character(*) :: file
integer :: line
if(fail) then
print *, 'Assertion ', text, &
' failed in file ', file, &
' at line ', line, &
'.'
stop
end if
end subroutine
end module
When I compile it with:
gfortran -c Assert_Mod.F90
gfortran -o check Assert_Mod.o Check_Assert.F90
I get the following error message:
Check_Assert.F90:6:31:
6 | Assert(1>2);
| 1
Error: Syntax error in argument list at (1)
If I do exactly the same thing with Intel Fortran, all works fine and program gives expected output:
Assertion 1>2 failed in file Check_Assert.F90 at line 6 .

Does anyone have a clue what is going on with GNU Fortran? Why can't it swallow the assert in the way I defined it, and in the way it works for Intel?
Any help or hint would be appreciated.
Cheers

It has to do with the second argument of your macro #X and how the Fortran expression is cast into a string. If you remove that from your macro and subroutine definition you should be good to go
module Assert_Mod
contains
subroutine Handle_Assert(fail, file, line)
logical, intent(in) :: fail
character(*), intent(in) :: file
integer, intent(in) :: line
if(fail) then
print *, ' failed in file ', file, &
' at line ', line, &
'.'
stop
end if
end subroutine Handle_Assert
end module
program Check_Assert
use Assert_Mod
#define Assert(X) call Handle_Assert(.not.(X), __FILE__, __LINE__)
Assert(1>2)
end program
On a related note, I would urge against using preprocessor macros to define an assert function (even though I do the same sometimes). Here is an example for floating point assertions that you can draw inspiration from: https://scivision.github.io/fortran2018-examples/sourcefile/assert.f90.html
and here is a more involved error framework which also reports the stack trace: https://fortran-lang.discourse.group/t/fortran-error-handling-including-stacktrace-generation/4648
There are also a lot of other assertion/testing libraries in Fortran that might be better suited for your case.

OK folks, thanks for the attention, I managed to work around as this:
#if __GFORTRAN__ == 1
# define Assert(X) call Handle_Assert(.not.(X), "X", __FILE__, __LINE__)
#else
# define Assert(X) call Handle_Assert(.not.(X), #X, __FILE__, __LINE__)
#endif
I am not sure how elegant you find it or how robust this is, but works as expected.

Related

How to send command line input to a Fortran subroutine? [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 skip lines when some conditions are met with Fortran? [duplicate]

It is my understanding that Fortran, when reading data from file, will skip lines starting with and asterisk (*) assuming that they are a comment. Well, I seem to be having a problem with achieving this behavior with a very simple program I created. This is my simple Fortran program:
1 program test
2
3 integer dat1
4
5 open(unit=1,file="file.inp")
6
7 read(1,*) dat1
8
9
10 end program test
This is "file.inp":
1 *Hello
2 1
I built my simple program with
gfortran -g -o test test.f90
When I run, I get the error:
At line 7 of file test.f90 (unit = 1, file = 'file.inp')
Fortran runtime error: Bad integer for item 1 in list input
When I run the input file with the comment line deleted, i.e.:
1 1
The code runs fine. So it seems to be a problem with Fortran correctly interpreting that comment line. It must be something exceedingly simple I'm missing here, but I can't turn up anything on google.
Fortran doesn't automatically skip comments lines in input files. You can do this easily enough by first reading the line into a string, checking the first character for your comment symbol or search the string for that symbol, then if the line is not a comment, doing an "internal read" of the string to obtain the numeric value.
Something like:
use, intrinsic :: iso_fortran_env
character (len=200) :: line
integer :: dat1, RetCode
read_loop: do
read (1, '(A)', isostat=RetCode) line
if ( RetCode == iostat_end) exit ReadLoop
if ( RetCode /= 0 ) then
... read error
exit read_loop
end if
if ( index (line, "*") /= 0 ) cycle read_loop
read (line, *) dat1
end do read_loop
Fortran does not ignore anything by default, unless you are using namelists and in that case comments start with an exclamation mark.
I found the use of the backspace statement to be a lot more intuitive than the proposed solutions. The following subroutine skips the line when a comment character, "#" is encountered at the beginning of the line.
subroutine skip_comments(fileUnit)
integer, intent(in) :: fileUnit
character(len=1) :: firstChar
firstChar = '#'
do while (firstChar .eq. '#')
read(fileUnit, '(A)') firstChar
enddo
backspace(fileUnit)
end subroutine skip_comments
This subroutine may be used in programs before the read statement like so:
open(unit=10, file=filename)
call skip_comments(10)
read(10, *) a, b, c
call skip_comments(10)
read(10, *) d, e
close(10)
Limitations for the above implementation:
It will not work if the comment is placed between the values of a variable spanning multiple lines, say an array.
It is very inefficient for large input files since the entire file is re-read from the beginning till the previous character when the backspace statement is encountered.
Can only be used for sequential access files, i.e. typical ASCII text files. Files opened with the direct or append access types will not work.
However, I find it a perfect fit for short files used for providing user-parameters.

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

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

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.

Test whether a directory exists or not

I'm trying to verify that a directory exists using Fortan90. On various sites I found:
logical :: dir_e
inquire(file='./docs/.', exist=dir_e)
if ( dir_e ) then
write(*,*) "dir exists!"
else
! workaround: it calls an extern program...
call system('mkdir docs')
end if
However, inquire returns False whether or not the directory exists and if I execute this code twice, I get an error message
cannot make dir, file already exists
If I use:
inquire(file='./docs/test', exist=dir_e)
with an existing file test, inquire returns true.
How can I check for the existence of a directory? I am using ubuntu 11.04 and the ifort compiler.
The Fortran standard 95, 2003 and 2008 do not specify, how inquire should treat directories. From my experience under Linux, gfortran treats them as files, ifort does not. The directory statement is a proprietary feature of ifort and should therefore be avoided.
The safest would be to test for a file in the said directory.
The following should work:
INQUIRE (DIRECTORY=dir, EXIST=ex [, DIRSPEC=dirspec] [, ERR=label] [, IOSTAT=i-var] )
I don't have ifort on this machine so I can't test it.
Addendum: The code posted originally works with gfortran. The DIRECTORY statement works with ifort but not with gfortran.
And in case for more information check: http://software.intel.com/sites/products/documentation/hpc/compilerpro/en-us/fortran/win/compiler_f/lref_for/source_files/rfinquir.htm#rfinquir
Most of the time, one checks if the directory exists so to write something in it. What I do is just create the directory. If it already exists there is no problem.
CALL system("mkdir video")
CALL chdir("video")
CALL getcwd(path)
You could use C routines to test the files :
C side (OK with ifort and gfortran on Win32 and Linux 32/64)
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#if defined(_WIN32) && defined(__INTEL_COMPILER)
# include "dirent_windows.h"
#else
# include <dirent.h>
#endif
void file_info(const char*filename,int*mode,int*exist,int*time){
int k;
struct stat buf;
k=stat(filename,&buf);
if(k != 0) {
*mode=0;
*exist=0;
*time=0;
}else{
*mode=buf.st_mode;
if(*mode == 0) *exist=0; else *exist=1;
*time=buf.st_mtime;
}
}
Fortran side :
MODULE file
USE iso_c_binding
INTERFACE
SUBROUTINE file_info(filename,mode,exist,time) BIND(C,name="file_info")
USE iso_c_binding
CHARACTER(kind=C_CHAR),INTENT(in) :: filename(*)
INTEGER(C_INT),INTENT(out) :: mode,exist,time
END SUBROUTINE
END INTERFACE
END MODULE
How to use in a Fortran routine :
..
use file
use iso_c_binding
...
integer(c_int) :: mode,exist,time
...
call file_info("./docs"//char(0),mode,exist,time)
Advantage : it works for any kind of file and provides additional pieces of information like the mode (read/write/execute permission) and the creation time.
Here's a subroutine I use often -- it uses the conditional you asked about:
subroutine create_directory( newDirPath )
! Author: Jess Vriesema
! Date: Spring 2011
! Purpose: Creates a directory at ./newDirPath
implicit none
character(len=*), intent(in) :: newDirPath
character(len=256) :: mkdirCmd
logical :: dirExists
! Check if the directory exists first
! inquire( file=trim(newDirPath)//'/.', exist=dirExists ) ! Works with gfortran, but not ifort
inquire( directory=newDirPath, exist=dirExists ) ! Works with ifort, but not gfortran
if (dirExists) then
! write (*,*) "Directory already exists: '"//trim(newDirPath)//"'"
else
mkdirCmd = 'mkdir -p '//trim(newDirPath)
write(*,'(a)') "Creating new directory: '"//trim(mkdirCmd)//"'"
call system( mkdirCmd )
endif
end subroutine create_directory
Depending on which compiler you use, you'll have to decide which of those conditionals is right for you.
Unfortunately, I do not have access to nagfor and don't know how it treats directories.
Another non-portable solution is to let the shell (Bash, in this case) do the work:
call system('[[ ! -e docs ]] && mkdir docs')
I had the same problem. If you want a compiler independent way of doing this, you can try to open a small file within the directory. The open statement allows for the code to jump to a particular line (specified by err=) if the open statement fails:
! Tests whether the directory exists
subroutine checkdir(dir)
implicit none
character(len=*), intent(in) :: dir
integer :: unitno
! Test whether the directory exists
open(newunit=unitno,file=trim(dir)//'deleteme.txt',status='replace',err=1234)
close (unitno)
return
! If doesn't exist, end gracefully
1234 write(*,*) 'Data directory, '//trim(dir)//' does not exist or could not write there!'
STOP
end subroutine
Note that this is not foolproof, as it is assumed "dir" has the trailing "/" or "\" depending on the OS being used.