I've been given the challenge to port a Fortran 77 program into C#.
I've found out that read(5,*) read from the standard input, i.e. the keyboard.
Now I'm trying to understand how the following works:
1. When I run the program, I have to run it as cheeseCalc<blue.dat>output.txt
, which read a blue.dat file and produces a output.txt file. How does read work in this case?
In the same program, there is READ(5,* )IDUM and later it also has read(5,*)idum,idum,tinit. What is happening in this case?
The blue.dat file has the following lines:
HEAD make new cake
INPUT VARIABLES
MFED MASS-FEED 30 ;1001 1 100 PEOPLE TO FEED
TOVE TEMP-IN-OVEN 150.0 ;1001 20 100 TEMPERATURE OF OVEN, C
UPDATED: Just for context, the initial lines of code in the program are:
program cheeseCalc
CHARACTER*76 IDENT
CHARACTER*1 IDUM
READ(5,104)IDENT
104 FORMAT(4X,A)
READ(5,*)IDUM
c write start record
write(6,102)IDENT
102 format('**START',/,4X,A,/)
read(5,*)idum,idum,frate
110 format(f10.0)
frate2=frate/3.6
read(5,*)idum,idum,tempo
* Do calculation *
write(6,*)frate2,tempo
end
Any help will be appreciated!! Thanks!
The full detail of the general read statement is documented elsewhere, but there is an idiom here which is perhaps worth elaborating on.
The statement read(5,*) ... is list-directed input from the external unit number 5. Let's assume (it's not guaranteed, but it's likely and you seem happy with that for your setup) that this external unit is standard input.
The idiomatic part is the repeated use of a single variable in an input list such as
read(5,*) idum, idum, ...
This (and the fact that idum is an (awfully named) length-1 character variable) signifies that the user doesn't care about the input in the first two fields). The first string, delimited by blanks, is read then the first character is assigned to idum. Then idum is immediately set to the first character of the next string.
The purpose of this is to set the place in the record to the third field, which is read into the (real) variable frate (in the first case).
Equally
read(5,*) idum
is just skipping the second line (strictly, reading the first character, but that's not used anywhere before the next read into idum): the first blank-delimited field is read but the next read moves on to the next line rather than continuing with that one.
Related
I have large text files with about 6 lines/instances of 3_xcalc_59 in which 59 is some 2-digit integer.
I am looking to increment these values of the text file by 1 every time I run the program.
I know I can increment a value defined in the code, but how can I increment an integer inside a line of text?
I was thinking the first part of the process would involve reading these lines and assigning them to string variables or a list, but I am not sure how to even do that.
I can find the lines by writing if line.startswith("3_xcalc") , but I'm not sure how to assign them to a list.
Simply writing
for line in open(inputfile, "w"):
line.startswith("3_xcalc") = listoflinesstartingwith3xcalc
Tells me "can't assign to function call", so that doesn't work, but I'm not sure what else to try.
Thank you.
I have little knowledge of the Fortran language. I have come across some programs written in the 90s (see attached snapshot showing just a portion of a long script).
I'd like to know what kind of compiler is appropriate to execute such codes? I have installed gfortran-4.2.3 on my mac. I'm also not sure about the indentation in the attached code: if C (comment) is at column 1, does the main code start at column 9 or 7? what about the position of numbers placed in between referred by GO TO?
This is not Fortran 90. This is Fortran 77.
That said, gfortran is able to compile this code. Make sure that the file extension for the file is .f so that gfortran realises it's fixed-form.
The C needs to be in the first column, the numbers that you reference are labels, they can be between column 1 and 5. The asterisk at line 198 is a continuation character, meaning that this should be treated as part of the previous line. It must be in column 6. Everything else needs to be between columns 7 and 72 (inclusive)
Oh, and the 3-digit numbers at the very beginning are line numbers, and must not be in the source code.
Edited to add: Since you have to type it all again anyway, you might as well make it free-form:
Replace the C in the first column with !, and change the way continuation lines are marked: Turn this:
write (*, *) "This is some long text,
* which doesn't fit into a line"
Into this:
write(*, *) "This is some long text, " // &
"which doesn't fit into a line"
Everything else can stay like it is. (You can now use proper indentation, too!)
New Edit
So you've pasted the code that you wrote and the error messages, so I'm replying to that now.
In Fixed Form, any character past column 72 is ignored. You have a few lines with long strings, where the terminating quotation mark is in that ignored region.
Make sure that no line exceeds the 72nd row.
Hy everybody, I've found some problems in reading unformatted character strings in a simple file. When the first / is found, everything is missed after it.
This is the example of the text I would like to read: after the first 18 character blocks that are fixed (from #Mod to Flow[kW]), there is a list of chemical species' names, that are variables (in this case 5) within the program I'm writing.
#Mod ID Mod Name Type C. #Coll MF[kg/s] Pres.[Pa] Pres.[bar] Temp.[K] Temp.[C] Ent[kJ/kg K] Power[kW] RPM[rad/s] Heat Flow[kW] METHANE ETHANE PROPANE NITROGEN H2O
I would like to skip, after some formal checks, the first 18 blocks, then read the chemical species. To do the former, I created a character array with dimension of 18, each with a length of 20.
character(20), dimension(18) :: chapp
Then I would like to associate the 18 blocks to the character array
read(1,*) (chapp(i),i=1,18)
...but this is the result: from chapp(1) to chapp(7) are saved the right first 7 strings, but this is chapp(8)
chapp(8) = 'MF[kg '
and from here on, everything is leaved blank!
How could I overcome this reading problem?
The problem is due to your using list-directed input (the * as the format). List-directed input is useful for quick and dirty input, but it has its limitations and quirks.
You stumbled across a quirk: A slash (/) in the input terminates assignment of values to the input list for the READ statement. This is exactly the behavior that you described above.
This is not choice of the compiler writer, but is mandated by all relevant Fortran standards.
The solution is to use formatted input. There are several options for this:
If you know that your labels will always be in the same columns, you can use a format string like '(1X,A4,2X,A2,1X,A3,2X)' (this is not complete) to read in the individual labels. This is error-prone, and is also bad if the program that writes out the data changes format for some reason or other, or if the labes are edited by hand.
If you can control the program that writes the label, you can use tab characters to separate the individual labels (and also, later, the labels). Read in the whole line, split it into tab-separated substrings using INDEX and read in the individual fields using an (A) format. Don't use list-directed format, or you will get hit by the / quirk mentioned above. This has the advantage that your labels can also include spaces, and that the data can be imported from/to Excel rather easily. This is what I usually do in such cases.
Otherwise, you can read in the whole line and split on multiple spaces. A bit more complicated than splitting on single tab characters, but it may be the best option if you cannot control the data source. You cannot have labels containing spaces then.
I have a file which is a table of 119 columns (separated by spaces) and around 50000 rows (lines). I would like to remove the duplicated entries, i.e. those rows which have all identical columns (119). I sketched this code:
PROGRAM deldup
IMPLICIT NONE
DOUBLE PRECISION PAR(119),PAR2(119)
INTEGER I,J,K,LINE,TREP
CHARACTER filename*40
c Get the input file name
CALL getarg(1,filename)
c File where the results will be stored.
OPEN(29, FILE="result.dat", STATUS='UNKNOWN')
c Current line number
LINE=0
c counting repeated points
TREP=0
101 LINE=LINE+1
OPEN(27, FILE=filename, STATUS='OLD')
c Verifying that we are not in the first line... and we read the
c corresponding one
IF (LINE.NE.1) THEN
DO K=1,LINE-1
READ(27,11,ERR=103,END=9999)
END DO
ENDIF
READ(27,11,ERR=103,END=9999) (PAR(I),I=1,119)
c Start comparing line by line looking for matches. If a match is
c found , close the
c file and open it again to read the next line. If the end of file is
c reached and not iqual rows found, write the line in "results.dat"
102 READ(27, 11,END=104, ERR=102) (PAR2(I),I=1,119)
DO J=1,119
IF ( PAR(J).NE.PAR2(J) ) THEN
GOTO 102
ELSEIF (J.EQ.119) THEN
TREP=TREP+1
GOTO 103
ENDIF
END DO
104 WRITE(29,11) (PAR(I),I=1,119)
103 CLOSE(27)
GOTO 101
9999 WRITE(*,*) "DONE!,", TREP, "duplicated points found!"
CLOSE(27)
CLOSE(28)
CLOSE(29)
11 FORMAT(200E14.6)
END
which actually works it is just super slow. Why? Is there any library that I can use? Sorry for my ignorance, I am completely new with Fortran77.
For each line you open and close the original file, which is very slow! To speed things up, you could just use rewind.
The main issue, though, is the complexity of your algorithm: O(n^2) [You compare each line to every other line]. As a start, I would keep a list of unique line, and compare against that list. If a new row is already listed, discard it - if not, it is a new unique row. This would reduce the complexity to O(n*m), with (hopefully) m << n (m is the number of unique rows). Sorting the rows will probably speed up the comparison.
The next remark would be to move from I/O to memory! Read in the complete file into an array, or at least keep the list of unique rows in memory. A 50,000x119 double precision array requires ~45MB of RAM, so I think this should be feasible ;-)
Write the result back in one piece in a final step.
First question: Why stick with Fortran 77? Since g95 and gfortran have come along, there is no real reason to use a standard that has been obsolete for more than twenty years.
The canonical way to remove duplicate is to sort them, remove duplicates, and then output them in the original order. If you use a good sorting algorithm such as quicksort or heapsort, this will give you O(n log n) performance.
One additional remark: It is also a good idea to put magic numbers such as 119 in your program into PARAMETER statements.
Suppose I have the following code
program fortran
open(900, FILE='SOMETHING')
write(900, *) '21'
end program fortran
The file form will be
21
that is, there is a space before the number. How to get rid of that space?
You can write it as a string:
PROGRAM fortran
OPEN(900,FILE='SOMETHING')
WRITE(900,'(a)') '21'
END PROGRAM FORTRAN
> cat SOMETHING
21
To respond to the comment:
The more explicit way of doing that would be to write the number into a string (you could also use list-directed I/O for this step), remove whitespaces from the string trim and finally output the left-adjusted adjustl:
program test
character(len=23) :: str
write(str,'(ES23.15 E3)') 1.23d0
write(*,'(a)') adjustl(trim(str))
write(str,'(ES14.7 E2)') 0.12e0
write(*,'(a)') adjustl(trim(str))
end program
> ./a.out
1.230000000000000E+000
1.2000000E-01
This solution is probably more complicated then necessary, but it is a very flexible approach that can be extended easily for arbitrary purposes and formats.
In list-directed format (the * in write(unit,*)) the compiler typically inserts a leading space. The first column used to be used to control line printers but that is now deleted from Fortran.
You can use any explicit format you want to get rid of the leading space. For example the general g0 one or the string specific a or the integer-specific i.