JNI for using fortran dll

JNI for using fortran dll

I'm new to this forum and was not sure where to post this question.  I've got some questions regarding setting up a JNI to access a Fortran dll.

I've built a Fortran dll from f77 from UNIX for the PC using the Intel compiler.  Now I need to access it from my Java code via C++ wrapper code.  I've searched online and most examples show you how to take a simple piece of Fortran code and nothing as complex as I really need.  My basic issue is that the Fortran code I used to make the dll is maintained by someone else.  So I am not really authorized to make any changes to it.  I'd like to be able to do everything I need inside the wrapper C++ code and simply call the dll to perform the necessary calculations.

That being said here are my questions:

1.  Is there some kind of table so I can match up the datatypes between the Java/C/Fortran codes?  The Fortran code is expecting reals, real arrays, int, int arrays, character*1, and character*(*).  I'm not sure how to specify these in the extern line in the C++ code.  Do I pass by reference or by value?

extern "C" void WIND(float*,float[],float[][],const char*....) ??

Java is of course passing floats, 2d float arrays, and strings.  I can't find an example that explains how to declare variables for the function prototype for the Fortran subroutine.

I know the conversion from Java to C++ code has items such as jstring, jobjectArray, and jfloatArrays which make sense and works fine for me.

2.  Assuming I can correctly call my Fortran subroutine from the dll, how to I get values back.  The subroutine accepts several input variables and returns several output variables.  I understand that I have to convert the java arrays into native arrays to pass to Fortan and vice versa, once the Fortan code has returned the output values, I must convert those arrays back into something I can pass back to Java.

I have Fortran subroutines and not Fortran functions so will I still be able to get back a bunch of ouptut variables the same way such as this?

WIND(inX,inY,inZ,outA,outB,outC)

3.  Assuming I can get back outA, outB, and outC does this mean I have to write getter/setter methods inside the C++ code so my java code can get back all of my data since Java limits me to one object for returning?  What's the best way to do this?  Should I just make global variables in the C++ code that get set once I get my data back from the Fortran dll call?  Then I could tell Java to call the getter methods after the initial run of the calculations have been completed?

Any suggestions any one can give me with be very much appreciated.  I have gotten a JNI working for some other C++ code so I just need help on how to extend this to work for a Fortran dll that I'm not allowed to alter.

publicaciones de 32 / 0 nuevos
Último envío
Para obtener más información sobre las optimizaciones del compilador, consulte el aviso sobre la optimización.

If you cannot alter the dll, but can build it from source, you could add some wrapper functions in Fortran that take C types for input and output and internally making the calls the fortran functions you cannot modify.  You could then call these wrappers from C and that solves the type compatibility issues.  For java to c, you mention you can only return one object, so use an object that contains all of the data you need to return (e.g. a struct).

I can't help with the code, as my knowledge of the c/fortran interoperability features are a bit lacking, though others here are very knowledgable on that subject.  

Samantha: Please specify the interface(s) to the Fortran DLL routine(s), following which I or someone else will reply with matching C function prototypes, etc. Giving you a more general reply would involve about as many pages as the mixed-language programming chapter of the Intel Fortran user guide.

You may obtain the interfaces in several ways: (i) show the subroutine declaration, and the declarations of the variables in its argument list; the author of the Fortran code is responsible for giving this to you, in order to make her/his routine callable; (ii) use the automatic interface generation capability of the IFort compiler or third party utilities such as Olagnon's F90AIB, available at http://www.ifremer.fr/ditigo/molagnon/fortran90/ ; this will have to be run by someone with access to the source code for the DLL.

Okay here's the subroutine call and variable declarations from Fortran:

subroutine wind (nl, cpcord, rel, alt, sp, seas, idtsel, unit, totdst, ews, ewd, standv, ews50p, fname, ialtfg, idl, altl, spdl, avalt, avspd, distlg, ierr)

     integer idtsel(5), unit, ialtfg(6), idl, nl, ierr
     real rel(3), spdl(251), sp(6), altl(251), alt(6), cpcord(2,nl), standv(6,5), ews(36,5), ews50p(12,5), distlg(251), avalt, avspd, totdst, ewd(36,5)

     character*1 seas
     character*(*) fname

The input variables are: alt, altl, cpcord, fname, ialtfg, idl, idtsel, nl, rel, seas, sp, spdl, standv, unit.

The output variables are avalt, avspd, distlg, ierr, ewd, ews, ews50p, and totdst.

Java needs to pass the inputs into the C++ code and the C++ needs to do all the necessary conversions to pass them into the Fortran dll call.  I need to get the values back and covert them back into the appropriate things for Java.  I'd like to figure out how to do this manually so I can understand the process for future as we have some other Fortran code from UNIX I need to interface with as well.

Here is the first attempt. Warnings: the code is just a skeleton, and entirely based on the description that you gave. Note, in particular, that I have no basis for assigning values to the input variables, so I have left them undefined. Also, all the arguments in the call that are arrays are to be allocated in advance, my code is going to pass uninitialized pointers. You may try linking the C code that I give to the real Fortran DLL; if the linking succeeds, please do not attempt to run the resulting program!

ifort /MD /Od /c wind.f
icl /MD /Od cwind.c wind.obj
cwind
Wind returned ierr = 0

Note that I have said nothing about Java. I think that it is best that you avoid the Java side until you get your C/C++ wrappers and your third party Fortran DLL working together.

Adjuntos: 

AdjuntoTamaño
Descargar cwind.c1023 bytes
Descargar wind.f746 bytes

Thanks for the help with the C code.  That is my sticky spot right now.  Will post back here when I've got something working.

Oh I just thought of one more thing.  For the 2d arrays should they be float** or is it still just float*?  I noticed that my 1d arrays were called jfloatArrays but that 2d arrays were called jobjectArrays when I ran javah to create my *.h file for the JNI.

Quote:

Samantha G. wrote:

For the 2d arrays should they be float** or is it still just float*? 

Declaring them as float** will probably not work. Given "float **a", a would be an array of pointers to 1-D arrays of type float. Fortran does not use such arrays; it does not use C pointers unless you use the Fortran 2003 C-Interoperability features. Since your Fortran DLL was probably not written using C-Interoperability, you will have to pass the addresses of contiguous memory blocks to it for those arguments that are arrays.

C has a rather relaxed (cavalier?) attitude about pointers. You can usually get away with casting pointers of the wrong type to pointers of the correct type in order to match declared prototypes for functions.

I'm not sure I understand what you mean by that.

I know for passing a 1d array I can just use something like this to convert my incoming Java jfloatArray into a pointer to that array.

jfloat* spPtr = env->GetFloatArrayElements(speed,0);

and then I can pass this pointer through to the wind Fortran call.

What do I need to do to translate the 2d array into something Fortran can read it?  I was told that 2d arrays are stored differently coming from Java than Fortran expects.

Sorry, my acquaintance with Java is too slight to help you there; you will need help from someone else on the Java-C connection.

In general, it is not enough to simply find a compatible pointer to pass. The target of the pointer must contain variables of types that are binary-compatible with Fortran and have the same memory layout of multidimensional arrays as Fortran.

Then do you know if I can take the jobjectArray from Java and change it into a C 2D array if I can pass a pointer to that array into Fortran?  Are the C and Fortran arrays compatible in terms in memory layout?

Quote:

Samantha G. wrote:

Are the C and Fortran arrays compatible in terms in memory layout?

The major differences are that Fortran will have a descriptor in addition to the data, wheras C will just have data and that Fortran uses column-major ordering and C uses row-major for elements of the array. 

I am still convinced the cleanest way to do this is to write a F03 module using C-Interoperability to sit between the F77 and C code.

Quote:

Samantha G. wrote:

Then do you know if I can take the jobjectArray from Java and change it into a C 2D array if I can pass a pointer to that array into Fortran?  

I am not competent to answer Java questions of that type. I do not even know what a jobjectArray is, although if needed I could do some searching and reading about it.

Quote:

Are the C and Fortran arrays compatible in terms in memory layout?

1-D arrays are. For native 2-D arrays of fixed size, C uses row-major storage order. Fortran uses column-major storage order. For adjustable size 2-D arrays, C often uses an array of pointers. Such arrays are not compatible with Fortran. Compatibility is obtained by mapping the C 2-D array to a C 1-D array, which is compatible with a Fortran 1-D array, which can then be mapped to a Fortran 2-D array.[/quote]

Okay somebody at work suggested I do an inplace matrix transpose like the one here:  http://www.geeksforgeeks.org/inplace-m-x-n-size-matrix-transpose/

I've written a routine now to take my (MxN) jobjectarrays from Java and put them into  C matrices and do the inplace tranpose so the numbers are in the correct slots for Fortran to read them.  Essentially changing the row major matrix in C to a column major matrix in Fortran.

So that part is working but now my C code is giving me strange errors.  I'm trying to pass my integer values and integer arrays from Java into my Fortran call.  It's saying I'm doing an invalid conversion from jint* to an int* which I don't understand.

For the Fortran extern declaration statement I need to use the int* declaration for my int values and my int arrays.  The usual procedure for passing 1D arrays is to take the java array and create a pointer to that array and then pass that pointer array to Fortran.  I do this with a statement like this:

jint* ialtfgPtr = env->GetIntArrayElements(altitudeCompError,0);

Where I then do my Fortran call:

wind(.....,ialtfgPtr,....) that corresponds to my declaration statement at the top of extern "C" void wind(....,int *ialtfgPtr,....)

jint is supposed to be a typedef for int32_t.  Is this incompatible for Fortran?

You problem is that ialtfgPtr is of type jint* and your function wind() is delcared to take type int*.  If the underlying types are the same, change your call:

wind(.....,ialtfgPtr,....)

to

wind(.....,(int*) ialtfgPtr,....)

To cast your jint pointer to an int pointer so your argument matches your definition.   

Thanks Casey that was my problem.  So now I finally got the inputs from Java correctly converted to pass to my Fortran dll call.

So now that I'm building it's saying I have an undefined reference to WIND.  So now I'm confused.  I declare the fortran function prototype and then pass in the correct variables to the Fortran dll call.

I'm loading the Fortran dll from inside Java.  Is there something else I need to do inside the C++ code?  Or am I thinking about this wrong.  Do I need to do something to load the dll in C++?  Or is it a linker option during build time instead?

This is how I'm building the C++ code now.  I"m building the C++ into a dll for Java called "WindTempJNI.dll".  The actual Fortran dll is called "wndtmp.dll".

g++ -m64 -IC:\Progra~1\Java\jdk1.7.0_21\include -IC:\Progra~1\Java\jdk1.7.0_21\include\win32 -c -o WindTempJNI.o src/com_boeing_apt_wndtmp_WindTemp.cpp

g++ -m64 -Wl,--add-stdcall-alias -mwindows -shared -o WindTempJNI.dll WindTempJNI.o

Should I build a C++ dll that includes the Fortran dll and then just have Java use the C++ dll?  So it should be Java only talks to C++ and C++ only talks to Fortran so Java never knows about the Fortran dll.

Samantha

Adjuntos: 

The c++ will have to be linked against the fortran dll for WIND() to resolve.  If the java is only talking to c++, it does not need to know about the fortran dll.  I agree with your final statement, you should build a c++ dll that is linked against the fortran dll, and the java should interface with the c++ dll only.

I don't supposed you guys know how to do this or have a link to an example of how to do the bulid do you?

Do I build before the fact or do I load the dll at runtime?

The link below has some info on this (in a gcc/cygwin/windows environment suggested by your previous post):

http://cygwin.com/cygwin-ug-net/dll.html

Let me see if I can help move this a bit farther along. I built a DLL incorporating Samantha's CPP file and my Fortran file as follows.

ifort /MD /Od /c wind.f
icl /Od /LD /I. com-boeing-apt-wndtmp-windtemp.cpp wind.obj

The DLL got built, and exports the symbol _Java_com_boeing_apt_wndtmp_WindTemp_windJNI@96. Note that there is no need to build two DLLs, although that could be done. Please try using this DLL from your Java code.

Hi Guys,

Just thought I'd give you an update of where I am.  I think I've finally figured out this TurDuckIn of a problem of going from Java -> C++ -> Fortran.

I am trying to build my C++ dll using both Visual Studio and G++.  I got both to build and tried running them.  Of course both are causing Java to crash in two different places.  I'm still debugging the problem but at least I can see the end in sight.

Here are my findings for my G++ compiler:
I found out for this to work I have to build my Fortran library as a static library *.lib and not as a *.dll.  I then take this *.lib file and put in into my link command.  The hang up of course is the use of the Intel libraries of the Fortran compiler.  I had to tell the Intel compiler not use them with /nolibdir.  After that I had to manually find the specific Intel libraries on my computer and put them into my link statement  like this:

g++ -m64 $(CPFLAGS) -o WindTempJNI.dll WindTempJNI.o wndtmplib.lib libifcoremd.lib libmmds.lib libirc.lib

The code is crashing when it calls the Fortran method from my *.lib file.  I suspect I miss still be missing a library in the linker.

For the Visual Studio compiler:
The Intel compiler integrates into my Visual Studio so it make the build process easier.  The compiler build the C++ dll quite easily.  Of course it's having problems with my in place Matrix transpose method inside my C++ file.  I'm not sure why it's giving me an unhandled exception from this method in my C code.  On the bright side, when I tell it not to transpose my matrices and send it into Fortran, the code can run without crashing. So it seems it's talking to the Fortran all right.  I still need to verify that I get numbers back from my Fortran call.

Thanks for the help everyone.  Will let you know if I ever get this thing to work right and give me the correct numbers back.

Well the good news is that now the Visual Studio C++ dll build is running without errors.  I just needed to increase my heapsize to handle the in matrix transpose for my larger arrays.  The bad news is that there is no change in the numbers.  I pass in values that are zero and I am getting zero values back instead of nonzero values.  Does anyone see anything obviously wrong?

    WIND(nl,cpcoord,relPtr,altPtr,spPtr,seas,(int*) idtselPtr,unit,&totalDistance,ews,ewd,standv,ews50p,
            fname,(int*) ialtfgPtr,idl,altlPtr,spdlPtr,&averageAltitude,&averageSpeed,distlgPtr,ierr ,&seaslen,&fnamelen);

    //Outputs****************
    std::cout<<"totdst "<<totalDistance<<std::endl;
    std::cout<<"ews "<<ews[0]<<std::endl;
    std::cout<<"ewd "<<ewd[0]<<std::endl;
    std::cout<<"ews50p "<<ews50p[0]<<std::endl;

Adjuntos: 

From your last CPP file I see that your prototype for the Fortran function WIND has one error: Intel Fortran expects the lengths of CHARACTER arguments to be made available as hidden arguments added to the end of the original argument list, and these string lengths are passed by value.

Therefore instead of ",size_t* seaslen, size_t* fnamelen" you should have "size_t seaslen, size_t fnamelen" (corrected following Dr. Fortran below).

Please post the Java code that calls the C++/Fortran DLL, so that we can see if the Fortran subroutine is receiving the intended argument values.

Thanks mecej4, I missed that.  Good news everybody!  I can report that all the hookups are working now.  I did a temporary hack of the code to confirm that I am getting inside the Fortran subroutine.  That worked.  However the Fortran subroutine is not performing the calculations correctly.  I need to investigate that all the inputs made it to the other side correctly.  I'm glad the hard part has been done.  I can debug Fortran code in my sleep.

If I could, I'd buy beers for all you guys!

Quote:

mecej4 wrote:

Therefore instead of ",size_t* seaslen, size_t* fnamelen" you should have "int seaslen, int fnamelen".

Almost.  size_t is correct, not int. But no *.  So, "size_t seaslen, size_t fnamelen". If you know you'll always be running a 32-bit application, you can get away with int but I don't recommend it.

Samantha, I'm glad that we've helped you make such good progress! Be sure to tell your bosses what good support you got from Intel and its user forums! (See why I kept steering you back to the forum rather than your private messages to me?)

Steve - Intel Developer Support

I have attached my latest C++ code and the calling Java class.

After doing some debugging this morning, I've figured out the problem.  The input file it's trying to read swind.dat which is a binary file was written using another Fortran compiler.  I need to contact the group that created the input file and get another file created using the Intel or compatible compiler.  I experienced this issue before when I was porting Fortran code from HPUX to LINUX.  The systems were using different Fortran compilers to write and read their binary files.  I had to regenerate the binaries on the new system before the newly compiled code could read them.

I'm trying to build this for a 64bit application.

Will do Steve.  BTW, I tried to upload my file "WindTemp.java" but the webpage refused to let me saying it was an invalid file type.  I had to change the name to "WindTemp.java.txt" for the website to take it.

Adjuntos: 

You can always put files into a ZIP for uploading.

Which other Fortran compiler? Intel Fortran can handle unformatted data from most, with some options possiblt needed.

Steve - Intel Developer Support

Steve

Most of our codes and binary input files are built on HPUX compilers.  They use f90 or gcc.  When I was building on the LINUX side, I was building using ifort and gcc since f90 was not installed there.  Unfortunately a lot of our codes were a mix of f77 and f90 code so sometimes the gcc compiler was insuffcient for getting everything to work correctly.

When we need this code on the PC side we have an old xp machine that contained MS Powerstation and Digital Fortran.  The old compilers were being used because of downstream requirements of old programs.  Our office is hodge podge of lots of code on various compilers which makes things a lot more difficult than it has to be.

Our Fortran gurus here have somehow made sense of it all and gotten them to work.  Our challenge these days is trying to get our old codes to talk to our new codes like Java.

If you guys could build some magical Fortran to Java converter that would be awesome. :)

Ewww - that's the wrong direction! How about converting all that yucky and security-problematic Java to nice and fast Fortran?

If you have unformatted files written by PowerStation, compile the Fortran routine that opens the file with the /fpscomp:ioformat option. I suggest writing a small program that opens the file with the option, then calls a routine in a file compiled normally to write it back out in the Intel format.

HPUX can be handled by using CONVERT='BIG_ENDIAN' on the OPEN.

Steve - Intel Developer Support

Quote:

Steve Lionel (Intel) wrote:

Ewww - that's the wrong direction! How about converting all that yucky and security-problematic Java to nice and fast Fortran?

Haha.  If it was up to me, everybody would be running Fortran from the linux cluster and we'd toss out all the PCs.  No more pretty GUIs and force everybody to use command prompts.  However we have a requirement to run a GUI interface on Windows WITHOUT an internet connection.

I did successfully use the convert option on LINUX.  I need to talk to the creator of these input files to see if he can fix the files for me or talk to the others about updating the Fortran code.  Sadly that's the way it goeswhen you are trying to use somebody else stuff.

If you know that all the input files to be converted were written as unformatted Fortran files on HP UX using code compiled by the native F90 compiler, it would be a fairly simple matter to write a utility to convert the files to the native format of Intel Fortran on Windows.

Okay last update.  Code is running now and returning the correct values.  I rebuilt the program that wrote the binary input files that I needed using the Intel Fortran compiler.  I had to use /assume:byterecl flag on this program and on my Fortran library wndtmp and everything works now as intended.

I'm uploading a zip file of my final Java, cpp, and h files.  I moved my Java code into a Junit test to compare the values my Java code gets back from the Fortran library against the UNIX version of the code an an old VB6 version of the code.  Everything is within a tolerance of 0.1 which is acceptable.

Hope this helps somebody else who is unlucky enough to have to do this.

Adjuntos: 

AdjuntoTamaño
Descargar final.zip6.79 KB

Thanks for the update and the code example! I am sure it will help others.

Steve - Intel Developer Support

Deje un comentario

Por favor inicie sesión para agregar un comentario. ¿No es socio? Únase ya