- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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".
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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(*,...).
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks a lot to everyone. Steve's sugegstion worked like a charm. You guy are great...
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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).
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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)'.
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page