Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
28705 Discussions

Read statements (any suggestion is appreciated)

intelisgor
Beginner
993 Views

Hello all:

I have a weird problem - and I am having difficulty solving it. I hope someone can help me.

I have a Fortran engine (compiled in Visual Fortran) which does mathemtical calculataions (Finite element analysis). The engine takes a text file for input data by reading the file using "read" commands. For a single run (i.e. one file one analysis), this is working like a charm.

The problem is that I am now solving a nonlinear problem which requires thousands of cases to be run. For this, I recomplied the fortran engine as a DLL, and I wrote C++ program to call the DLL each time it needs to run it (The c++ engine is creating a text file each time it needs to run the engine). The problem is that the reading and writing to the text file is making the process so inefficent that I have to spend a couple of days running a single nonlinear problem. The bottleneck is reading and writing the files to the hard drive.

So I changed the c++ program such that it passes the input data to the Fortran engine as a string (formatted exactly like the text file.) However, at this point I don't know what to do. The Fortran engine is programmed to read the data from a text file using the read statements. It is going to be very challenging to modify my Fortran source code, so I am looking for the least intrusive way of handling this problem. My specific question is :

"Is there a way that the read stetements can be fooled to read from a unit in the memory (other than the text file, i.e. the string that I am passing from the C++ code)? If there is, then I don't need to change anything, and just point the input unit to that direction.

If this is not possible, what is the easiest way of handling this problem?

I am in desperate need to solve this problem ASAP. Any advice is appreciated.

Best,

Burki

0 Kudos
14 Replies
Steven_L_Intel1
Employee
993 Views

You can't "redirect" the READ but you can replace the unit number with the name of a character variable and it will "read" from that without other changes.This is a standard Fortran feature called "internal I/O". If the text is multiple lines, you'll have to create a character array with one element per "record".

0 Kudos
intelisgor
Beginner
993 Views

Dear Steve,

Thank you for your reply - this is probably my answer. One question though... I have hundreds of lines, each is read by a seperate formatted READ statement. Will "line breaks" be considered as the end of record and be enough for the READ statement to recognize it as an array? Or should I pre-process the data so that they are stored in an array?

Regards,

Burki

0 Kudos
Jugoslav_Dujic
Valued Contributor II
993 Views
I'm not sure how the Fortran internal READ will treat CRLFs (CHAR(13)//CHAR(10) or ) within the string -- you can try it yourself

Alternatively, instead of internal read, you can use Windows pipes to redirect the standard input and/or output. Please see the attached example -- it uses two different processes ("main" launching "submodule") to establish "talking" through the pipe, but it should work with a dll as well. The only change it would require in the Fortran code is to replace READ(Unit,...) with READ(*,...).
0 Kudos
Steven_L_Intel1
Employee
993 Views

Fortran wil l not recognize the CR-LF pairs in the string as record delimiters. If you can convert the string into a CHARACTER array (with a length of each element sufficient to hold a line), one element per line (requires some added code to split out the records), then each element will be treated as if it were a separate line.

Jugoslav's pipe suggestion is good too.

0 Kudos
anthonyrichards
New Contributor III
993 Views
What are the contents of these 'text files'? Numerical only, or mixed alphanumeric?
0 Kudos
intelisgor
Beginner
993 Views
They are alfanumeric. However, when I read them in the fortran engine I read them as text lines and extract the numeric values myself. So they can be considered as text lines.
0 Kudos
intelisgor
Beginner
993 Views

Thank you Jugoslav:

I think this is a good idea as well. The only worry I have is about piping. I am calling the engine (exe) from Visual C++ using the shell command. For the dll version, I use the regular dll call.

Let's say I am usig the exe (through shell command)... How can I pipe the data file intothe engine withinVC++. I haveused Pipe before, but not within the source code - only on the console.

Thanks,

Burkan

0 Kudos
intelisgor
Beginner
993 Views

Hi Steve,

The problem is that the length of each record is notfixed. So I would not know how to create a character array. If I could do this it would be perfect since I donl;t have to change the code.

Burki

0 Kudos
Steven_L_Intel1
Employee
993 Views
You would declare the character array with a character length long enough to hold the longest record. Pad the end of shorter records with blanks. It's easy to put together a loop that looks for the next CR-LF pair and copies the string into an array element.
0 Kudos
jimdempseyatthecove
Honored Contributor III
993 Views

Or in lieu of copying records you can use subscripts into the string. After every extraction advance the pointer into the string to the character index following the next line feed (char(10)).

ReadPtr = 1! initialization
...
100 continue ! Read next "line"
if(ReadPtr .eq. 0) goto 999! eof
read(buffer(ReadPtr:), fmt) args, ...
ReadPtr = SCAN(buffer(ReadPtr:),char(10))
if(ReadPtr .ne. 0) ReadPtr=ReadPtr+1! get ready for next time
... ! store the data wherever
goto 100

Jim

0 Kudos
intelisgor
Beginner
993 Views

Thanks a lot to everyone. Steve's sugegstion worked like a charm. You guy are great...

0 Kudos
Jugoslav_Dujic
Valued Contributor II
993 Views
I'm glad that your problem is solved (wow, that was fast), just to reply to your previous question related with pipes (perhaps for the future): take a look at the source code of the main program -- it's just a few pipe creation and handle redirecting APIs, easily translatable to C++.

Once you redirect your own standard output to the pipe using SetStdHandle(STD_OUTPUT_HANDLE), you can just use printf to write to the pipe; once you do it, the child process will "wake up" and READ(*) those same data each time you write a record into the pipe. If you don't want to redirect your own standard output of the main application, you can use WriteFile or _fdopen followed by fprintf.

(Actually, Fortran basically lacks the equivalent of _fdopen, which triggered this thread. _fdopen is, IIRC a VC++ extension. There is, however, a similar VF extension -- see docs on USEROPEN specifier of OPEN statement).
0 Kudos
intelisgor
Beginner
993 Views

Thanks. This is useful stuff...

My problem is actually not fully solved. There is something wrong somewhere, but I cannot figure out.I created a 5 line data (20 charaters each) from my CV++ code, and passedthis to the DLL as a string. I want to test if my code will work. I take the data from VC++ with the variable "idata". Then I extract 20 character long substrings from "idata" and load them to a character array called "dummy". I am using dummy as an internal file and reading it with 5 read statements to a character array called "bummy" (simulating what I want to achive). Then, I write the contents of bummy to a textfile to see if theoperation was successful. I see 5 lines, but each line is the same (the first line is copied to all other four lines)...The problem is notin "dummy" array; it is propoerly pouplated with the right data line. the problem is with the read line. It seems that after each read,the cursor stays in the beginning o fthe line, and it keeps reading dummy[1]. Any help is appreciated. My test DLL is below.

!
! FUNCTIONS/SUBROUTINES exported from VBINTERFACE.dll:
!VBINTERFACE - subroutine
!
subroutine VBINTERFACE(idata)

! Expose subroutine VBINTERFACE to users of this DLL
!
!DEC$ ATTRIBUTES DLLEXPORT::VBINTERFACE
!DEC$ ATTRIBUTES STDCALL,REFERENCE,ALIAS:"vbInterface" :: vbInterface

character dummy(5)*20
character bummy(5)*20
character*(*) idata
integer length

length = len(idata) / 20

do 10 i=1,length
dummy(i) = idata(20*(i-1)+1 : 20*i)
10continue

read(dummy,*) bummy(1)
read(dummy,*) bummy(2)
read(dummy,*) bummy(3)
read(dummy,*) bummy(4)
read(dummy,*) bummy(5)

open(1,file="testFILE")

do 20 i=1,length
write(1,*) bummy(i)
20continue

close(1)

end subroutine VBINTERFACE

0 Kudos
Steven_L_Intel1
Employee
993 Views

Sorry, you can't do it quite that way. Each separate READ starts at the first element of dummy. What I was suggesting was more like this:

read (dummy,*) bummy

This reads all five elements of dummy and assigns the contents of each one to respective elements of bummy.

I will caution you that reading character data with list-directed formatting can be a problem, especially if there is any punctuation. Rather than * use '(A)'.

0 Kudos
Reply