This question already has answers here:
How do I format a PRINT or WRITE statement to overwrite the current line on the console screen?
(4 answers)
Closed 5 years ago.
For "flavorful" output during iteration procedure, I'd like to use following approach (I saw it for some program written in C++) in Fortran.
Every new iteration gives me the line in the console with corresponding information:
Iteration XX Accuracy X.XXXXE-XX Time spent XX seconds
I want every new line to replace the previous one (i.e. labels "Iteration" "Accuracy" ... etc. remain unchanged, while values change).
I tried different variants including
backspace(6)
backspace(0)
write (*,*) char(8)//'Accuracy ...'
Of course, first two lines give a runtime error and the last gives nothing.
How can I implement such an approach?
It's perhaps a bit rather complicated solution, but one option would be to use the ncurses library - http://genepi.qimr.edu.au/staff/davidD/
Below is a modification of the program testcurs.f90available for download from the linked page. It just displays a progress message which is being progressively updated. In order to test it, you will need also the ncurses.f90 module and then link with -lncurses, i.e.,
gfortran -c ncurses.f95
gfortran -o test testcurs.f95 -lncurses
The example (showing just the program itself, testcurs.f90 contains also the definition of initTest etc.):
program testcurses
use curses
use commands
type (C_PTR) :: iwin = C_NULL_PTR
integer (C_INT) :: key
integer :: istat, new_option=1, old_option=0
CHARACTER(LEN=1024) :: msg
call initTest(iwin, istat)
if (istat /= 0) then
write(*,'(a)') 'ERROR: initscr failed!'
stop
end if
ierr = wbkgd(iwin, curses_a_reverse)
ierr = erase()
ierr = attrset(curses_a_bold)
DO i = 10, 100, 10
WRITE(msg, '(''Progress '', I0, ''%'')') i
ierr = mvaddstr(20, 20, TRIM(msg) // C_NULL_CHAR)
ierr = refresh()
CALL SLEEP(1)
END DO
key = getch()
ierr = delwin(iwin)
ierr = endwin()
end program testcurses
Related
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.
I have a Fortran routine that opens a lot of text files write data from a time loop. This routine uses open with the newunit option, this unit is stored in an object in order to write things in files later. This works fine most of the time but when the program needs to open a large number N of files at the same time I get the following error:
**forrtl: severe (104): incorrect STATUS= specifier value for connected file, unit -1, file CONOUT$**
reffering to the first open function in createFiles subroutine. This error occurs whether the file already exists or not. I don't know if this might help but at this stage the new unit that should be generated would be -32768.
I include a minimal code sample with a "timeSeries" class including a routine that creates two files:
the first file fileName1 is opened and closed directy after writing stuff inside
the second file fileName2 is kept open in order to write things comùputed in a time loop later and closed at the end of the time loop
The example is composed of the two following files. It breaks for i=32639.
main.f90 :
program writeFiles
use TS
logical :: stat
integer :: i, istep, N, NtimeSteps
character(len=16) :: fileName1, fileName2
character(len=300) :: path
type(timeSeries), dimension(:), allocatable :: myTS
call getcwd( path )
path = trim(path) // '\Output_files'
inquire(directory = trim(path), exist = stat )
if (.not. stat) call system("mkdir " // '"' // trim(path) // '"' )
N = 50000
NtimeSteps = 100
allocate(myTS(N))
do i = 1, N
write(fileName1,'(a6,i6.6,a4)') 'file1_', i, '.txt'
write(fileName2,'(a6,i6.6,a4)') 'file2_', i, '.txt'
call myTS(i)%createFiles(trim(path),fileName1,fileName2)
end do
do istep = 1, NtimeSteps
#
#compute stuff
#
do i = 1, N
write(myTS(i)%fileUnit,*) 'stuff'
end do
end do
do i = 1, N
close(myTS(i)%fileUnit)
end do
end program writeFiles
module.f90 :
module TS
type timeSeries
integer :: fileUnit
contains
procedure :: createFiles => timeSeries_createFiles
end type timeSeries
contains
subroutine timeSeries_createFiles(this,dir,fileName1,fileName2)
class(timeSeries) :: this
character(*) :: dir, fileName1, fileName2
open(newunit = this%fileUnit , file = dir // '\' // fileName1, status = 'replace') !error occurs here after multiple function calls
write(this%fileUnit,*) 'Write stuff'
close(this%fileUnit)
open(newunit = this%fileUnit , file = dir // '\' // fileName2, status = 'replace')
end subroutine timeSeries_createFiles
end module
Any idea about the reason for this error? Is there a limitation for the number of files opened at the same time? Could it be related to a memory issue?
I'm using Intel(R) Visual Fortran Compiler 17.0.4.210
Windows has this interesting habit of not releasing all the resources for a closed file for a short time after you do a close. I have seen this sort of problem on and off for decades. My usual recommendation is to put a call to SLEEPQQ with a duration of half a second after a CLOSE when you intend to do another OPEN soon after on the same file. But you're not doing that here.
There's more here that is puzzling. The error message referring to unit -1 and CONOUT$ should not occur when opening an explicit file and using NEWUNIT. In Intel's implementation, NEWUNIT numbers start at -129 and go more negative from there. Unit -1 is used for PRINT or WRITE(*), and CONOUT$ is the console. STATUS='REPLACE' would not be valid for a unit connected to the console. That the newunit number would be -32768 is telling and suggests an internal limit for NEWUNIT in the Intel libraries.
I did a test of my own and see that if you use NEWUNIT and close the unit, the unit numbers go as low as -16384 before cycling back to -129. That's ok if indeed you're closing the units, but you're never closing the second file you open, so you're at least hitting a maximum number of NEWUNIT files open. I would recommend figuring out a different way of approaching the problem that didn't require leaving thousands of files open.
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.
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 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.