I inherited some code, for a Chemistry simulation. I have the code working, with the exception that the output functions WRITE and FORMAT are giving me a hard time. I was reading the gfortran online manual, and saw that gfortran does not support FORMAT, stating that it is non-standard, and not as configurable as WRITE. That's fine, I would happily replace it, but I am having trouble figuring out how to format FORTRAN output.
What I want, is to have text, preceding a variable. such as:
ZETA = 12.34
where ZETA is text, and 12.34 is the content of some variable.
or in C++, cout << "ZETA = " << ZETA ;
I've been reading old web pages, and trying a variety of things, but I'm not getting anywhere.
Consider this sample code:
PROGRAM MAIN
REAL ZETA1, ZETA2
ZETA1 = 2.173171
ZETA2 = 1.18530
WRITE(*,"(A6)" Zeta1:,ZETA1, "(2F7.4)")
C 10 FORMAT *( ZETA 1, ZETA 2*,2F7.4)
END
the C commented line, is an original formating line, which throws an error.
How could the output be formated to display
ZETA1 2.1731
ZETA2 1.1853
Any help, or insight would be greatly appreciated.
Some examples:
PROGRAM MAIN
REAL ZETA1, ZETA2
ZETA1 = 2.173171
ZETA2 = 1.18530
write (*, '( "Zeta1:", F7.4 / "Zeta2:", F7.4 )' ) ZETA1, ZETA2
write (*, '( 2 (A, F7.4 / ) )' ) "Zeta1", ZETA1,"Zeta2", ZETA2
write (*, 1010) ZETA1, ZETA2
1010 format ( "Zeta1=", F7.4 / "Zeta2=", F7.4 )
END program main
Related
Given an input that looks like the following:
123
456
789
42
23
1337
3117
I want to iterate over this file in whitespace-separated chunks in Fortran (any version is fine). For example, let's say I wanted to take the average of each chunk (e.g. mean(123, 456, 789) then mean(42, 23, 1337) then mean(31337)).
I've tried iterating through the file normally (e.g. READ), reading in each line as a string and then converting to an int and doing whatever math I want to do on each chunk. The trouble here is that Fortran "helpfully" ignores blank lines in my text file - so when I try and compare against the empty string to check for the blank line, I never actually get a .True. on that comparison.
I feel like I'm missing something basic here, since this is a typical functionality in every other modern language, I'd be surprised if Fortran didn't somehow have it.
If you're using so-called "list-directed" input (format = '*'), Fortran does special handling to spaces, commas, and blank lines.
To your point, there's a feature which is using the BLANK keyword with read
read(iunit,'(i10)',blank="ZERO",err=1,end=2) array
You can set:
blank="ZERO" will return a valid zero value if a blank is found;
blank="NULL" is the default behavior that skips blank/returns an error depending on the input format.
If all your input values are positive, you could use blank="ZERO" and then use the location of zero values to process your data.
EDIT as #vladimir-f has correctly pointed out, you not only have blanks in between lines, but also after the end of the numbers in most lines, so this strategy will not work.
You can instead load everything into an array, and process it afterwards:
program array_with_blanks
integer :: ierr,num,iunit
integer, allocatable :: array(:)
open(newunit=iunit,file='stackoverflow',form='formatted',iostat=ierr)
allocate(array(0))
do
read(iunit,'(i10)',iostat=ierr) num
if (is_iostat_end(ierr)) then
exit
else
array = [array,num]
endif
end do
close(iunit)
print *, array
end program
Just read each line as a character (but note Francescalus's comment on the format). Then read the character as an internal file.
program stuff
implicit none
integer io, n, value, sum
character (len=1000) line
n = 0
sum = 0
io = 0
open( 42, file="stuff.txt" )
do while( io == 0 )
read( 42, "( a )", iostat = io ) line
if ( io /= 0 .or. line == "" ) then
if ( n > 0 ) print *, ( sum + 0.0 ) / n
n = 0
sum = 0
else
read( line, * ) value
n = n + 1
sum = sum + value
end if
end do
close( 42 )
end program stuff
456.000000
467.333344
3117.00000
In Octave, I am reading very large text files from disk and parsing them. The function textread() does just what I want except for the way it is implemented. Looking at the source, textread.m pulls the entire text file into memory before attempting to parse lines. If the text file is large, it fills all my free RAM (16 GB) with text and then starts saving back to disk (virtual memory), before parsing. If I wait long enough, textread() will complete, but it takes almost forever.
Notice that after parsing into a matrix of floating point values, the same data fit into memory quite easily. So I'm using textread() in an intermediate zone, where there is enough memory for the floats, but not enough memory for the same data as text.
All of that is preparation for my question, which is about strread(). The data in my text files looks like this
0.0647148 -2.0072535 0.5644875 8.6954257
0.1294296 -8.4689583 0.6567095 144.3090450
0.1941444 -9.2658037 -1.0228742 173.8027785
0.2588593 -6.5483359 -1.5767574 90.7337329
0.3235741 -0.7646807 -0.5320896 1.7357120
... and so on. There are no header lines or comments in the file.
I wrote a function that reads the file line by line, and notice the two ways I'm attempting to use strread() to parse a line of data.
function dest = readPowerSpectrumFile(filename, dest)
% read enough lines to fill destination array
[rows, cols] = size(dest);
fid = fopen(filename, 'r');
for line = 1 : rows
lstr = fgetl(fid);
% this line works, but is very brittle
[dest(line, 1), dest(line, 2), dest(line, 3), dest(line, 4)] = strread(lstr, "%f %f %f %f");
% This line doesn't work. Or anything similar I can think of.
% dest(line, 1:4) = strread(lstr, "%f %f %f %f");
endfor
fclose(fid);
endfunction
Is there an elegant way of having strread return parsed values to an array? Otherwise I'll have to write a new function any time I change the number of columns.
Thanks
Your described format is a matrix with floating point values. In this case you can just use load
d = load ("yourfile");
which is much faster than any other function. You can have a look at the used implementation in libinterp/corefcn/ls-mat-ascii.cc: read_mat_ascii_data
If you feed fprintf more values than are in its format specification, it will reapply the print statement until it's used them up:
>> fprintf("%d %d \n", 1:6)
1 2
3 4
5 6
It appears this also works with strread. If you specify only one value to read, but there are multiple on the current line, it will keep reading them and add them to a column vector. All we need to do is to assign those values to the correct row of dest:
function dest = readPowerSpectrumFile(filename, dest)
% read enough lines to fill destination array
[rows, cols] = size(dest);
fid = fopen(filename, 'r');
for line = 1 : rows
lstr = fgetl(fid);
% read all values from current line into column vector
% and store values into row of dest
dest(line,:) = strread(lstr, "%f");
% this will also work since values are assumed to be numeric by default:
% dest(line,:) = strread(lstr);
endfor
fclose(fid);
endfunction
Output:
readPowerSpectrumFile(filename, zeros(5,4))
ans =
6.4715e-02 -2.0073e+00 5.6449e-01 8.6954e+00
1.2943e-01 -8.4690e+00 6.5671e-01 1.4431e+02
1.9414e-01 -9.2658e+00 -1.0229e+00 1.7380e+02
2.5886e-01 -6.5483e+00 -1.5768e+00 9.0734e+01
3.2357e-01 -7.6468e-01 -5.3209e-01 1.7357e+00
I need help understanding this 50 line program
implicit none
integer maxk, maxb, maxs
parameter (maxk=6000, maxb=1000, maxs=5)
integer nk, nspin, nband, ik, is, ib
double precision e(maxb, maxs, maxk), k(maxk)
double precision ef, kmin, kmax, emin, emax
logical overflow
read(5,*) ef
read(5,*) kmin, kmax
read(5,*) emin, emax
read(5,*) nband, nspin, nk
overflow = (nband.gt.maxb) .or. (nk.gt.maxk) .or. (nspin.gt.maxs)
if (overflow) stop 'Dimensions in gnubands too small'
write(6,"(2a)") '# GNUBANDS: Utility for SIESTA to transform ',
. 'bands output into Gnuplot format'
write(6,"(a)") '#'
write(6,"(2a)") '# ',
. ' Emilio Artacho, Feb. 1999'
write(6,"(2a)") '# ------------------------------------------',
. '--------------------------------'
write(6,"(a,f10.4)") '# E_F = ', ef
write(6,"(a,2f10.4)") '# k_min, k_max = ', kmin, kmax
write(6,"(a,2f10.4)") '# E_min, E_max = ', emin, emax
write(6,"(a,3i6)") '# Nbands, Nspin, Nk = ', nband, nspin, nk
write(6,"(a)") '#'
write(6,"(a)") '# k E'
write(6,"(2a)") '# ------------------------------------------',
. '--------------------------------'
read(5,*) (k(ik),((e(ib,is,ik),ib=1,nband), is=1,nspin), ik=1,nk)
do is = 1, nspin
do ib = 1, nband
write(6,"(2f14.6)") ( k(ik), e(ib,is,ik), ik = 1, nk)
write(6,"(/)")
enddo
enddo
This is a free format Fortran file. The name of the program is gnubands and rearranges numbers in an input (which the user specifies). I would like to know how this program operates. Here is what I do not understand. The program takes input from a file, it reads
ef, kmin,kmax,emin,emax,nband,nspin,nk
However, all of these variables are not found inside the input file. I opened the input file in vi and conducted a search using /. I do not obtain any results. Nevertheless, the program appears to correctly pick all values. What is happening?
Also, I do not understand the read format
read(5,*) (k(ik),((e(ib,is,ik),ib=1,nband), is=1,nspin), ik=1,nk)
I am not familiar with the syntax and would like to know what it is saying or any references.
Some tutorial PDF of SIESTA shows that the input for gnubands.f is something like this:
whose header part is to be read by the first four read statements of gnubands.f. With this input, the variables are set as
ef = -5.018...
kmin = 0.000...
kmax = 3.338...
emin = -25.187...
emax = 143.069...
nband = 18
nspin = 1
nk = 150
by giving the input file from the standard input (assumed unit number 5) as
gfortran -o gnubands.x gnubands.f
gnubands.x < your_data_file.bands
Note that there are (and should be) no keywords like "ef" or "EF" or "Ef" (capitalization does not matter), because the numbers are directly read into the variables in gnubands.f. This is in contrast to other cases like using XML files, where (human-readable) tags or keywords are embedded in the file itself (e.g., pseudopotential files used by Quantum ESPRESSO). I guess your confusion might be coming from the use of namelist for obtaining input values, which looks like
namelist /your_inp/ a, b, c
read( funit, nml = your_inp )
with an input file
&your_inp
a = 1.0
b = "method1"
c = 77
/
In this case, the variable names (here, a, b, and c) appear literally in the input file.
Historically, 5 (in your read(5,*)) is stdin, so either
(1)you are supplying the value, when you are running the code,
or,(2) I guess when you run the SIESTA, (gnuband is a postprocessor of that) it creates a file, possibly named fort.5. Check that.
I'm trying to read an input file with fortran but I get the following error at runtime:
At line 118 of file prog.f90 (unit = 53, file = 'data.dat')
Fortran runtime error: Bad repeat count in item 1 of list input
The data file is the following
3, 5, 3 %comment
%%%%%%%%%%%%%%
1d0, 0d0, 0d0 % comment
0d0, 0d0, 1d0
%%%%%%%%%%%%%%
1, 1, identity, 1, 1 %comment
1, 2, sigmax, 2, 2
2, 3, sigmax, 2, 2
1, 3, sigmaz, 1, 3
3, 3, identity, 1, 1
%%%%%%%%%%%%%%
0, 0 %comment
and the interesting part of prog.f90 is
COMPLEX(KIND(1D0)), DIMENSION(:), ALLOCATABLE:: H1, H2
INTEGER :: i,A,B,C
CHARACTER(50) :: GHOST
OPEN(UNIT=53,file='data.dat',status='old')
READ(53,*) A,B,C
READ(53,*) GHOST
ALLOCATE (H1(A),H2(A))
READ(53,*) (H1(i), i=1,A)
READ(53,*) (H2(i), i=1,A)
where the 118th line is READ(53,*) (H1(i), i=1,A). I tryed also with an explicit do loop but with the same result.
I haven't tested this, but I'd expect
READ(53,*) (H1(i), i=1,A)
to try to read 3 complex numbers. It gets fed the line
1d0, 0d0, 0d0 % comment
from which it gets 1½ complex numbers and then barfs on the % sign, misinterpreting it as a syntactically invalid repeat count.
I'd suggest providing 3 complex numbers in the file when that read statement is executed.
The numbers are dimensioned complex, while in fortran complex numbers should be in the file with parenthesis as:
( realpart , imaginarypart ) ( realpart , imaginarypart )
I really don't know what the standards say regarding the input form you have presented, but after some testing gfortran throws that Bad repeat count error regardless of the % comment. It throws that error even with four or more comma separated reals on the line.
Now ifort on the other hand reads the line just the way you have it -- but watch out -- it reads each of the comma separated values as the real part of your complex variable, setting the imaginary part to zero. ( that is it only uses the first two values on each line and discards the third )
You will really need to study the code to make sure you understand what was intended to sort out how to fix this. If the later (ifort) behavior is the intention one simple fix would be to declare a couple of reals. Read into the reals, then assign those to your complex variables.
I'm writing a task in pascal.
Everything is ok, just my result is not right.
I'm summing some numbers
Example: 2.3 + 3.4+ 3.3 = 9
But output shows: 9.000000 + EEE or something like that.
So- how to convert, to be only 9, not this REAL variable.
To actually convert:
var
i: integer;
...
i := round(floatVar);
To output only the integer part:
writeln(floatVar:9:0);
Let's consider this quite simpler equation:
3.5 + 2.5
What do you expect? 6, right? Let's try this code
write(3.5 + 2.5);
Unfortunately, it's a floating-point number, so it would produce a number represented in a scientific way:
6.00000000000E+00
or 6.0000000000 x 100, or 6 x 10o. Whatever, you only care about 6, who need this weird useless long number? So the idea is to cut off the decimal part and output to the console only the integer part, which can be done with this line of code:
write(3.5 + 2.5 : 0 : 0);
Ok, now it outputs a beautiful number as expected
6
Seems like the problem is solved, but you say that:
I'm summing some numbers
Example: 2.3 + 3.4+ 3.3 = 9
Ohh so that the evenly, beautiful integer is just randomly appeared? Here the problem comes, how do you expect this equation would output?
3.6 + 2.5
It should be 6.1, right? Let's try it with the worked line of code:
write(3.6 + 2.5 : 0 : 0);
And the output is...
6
Unexpected, right? So how about rounding to some decimal places, like 1?
write(3.5 + 2.5 : 0 : 1);
write(3.6 + 2.5 : 0 : 1);
Then, 3.5 + 2.5 = 6.0 and 3.6 + 2.5 = 6.1. But 6.0 may look quite long, so how to make it output 6 for 6.0 and 6.1 for 6.1?
Actually, you can't make the program auto-detect if a real variable contains an integer value because the way a real var is stored is completely different from an integer var (how different they are, please contact Google; but you can do it manually by making a function to do the job).
So my solution is, to be easy, making the output rounded to some decimal places, and that's it.
For purpose of showing pretty output on the screen you can use something like this:
Writeln(result:0:2);
Result on screen would be this:
9.00
What this means someone would ask? Well first number 0 means how wide filed is. if you say it's 0 then Pascal writes it at the very left side of screen. If you said writeln(result:5:2) result would be:
9.00
In other words i would print form the right side and leave 5 chars to do so.
Second number 2, in this example means you want that result printed with 2 decimal places. You can place it only if you want to print on screen value that is real, single, double, extended and so on.You can round to any number of decimals, and if you do writeln(result:0:0) you would get ouput:
9
If you are printing integer and want to have some length of field, lets sat 5 you would do: writeln(int:5). If you added :2 to the end you would get compile time error.
This all also works for something like this: writeln(5/3.5+sqrt(3):0:3),
You should know that this does not round variable itself but just formats output. This is also legal:
program test;
var
a:real;
n,m:integer;
begin
readln(a,m,n);
writeln(a:m:n);
end.
What i did here is i asked user if on how many decimals and with what length of field he wants to write entered number a. This can be useful so i'm pointing it out. Thank you for reading. I hope i helped
You can convert to string, get the int part, e convert to int number!
Or Float to Str than Str to Int:
nPage := StrToInt(FloatToStr(Int(nReg / nTPages))) + 1;