letsthinkabout.us

Bought cans of tuna for dinner. Came home & found out I bought cat food instead :(

Calling Fortran assemblies from .NET

Note: This has not been tested in mono or a 32-bit system.

You'll need: 1) Visual Studio 2) a good text editor 3) gfortran

the code in this post is available via GitHub here


“I don't know what the language of the year 2000 will look like, but I know it will be called Fortran.” —Tony Hoare, winner of the 1980 Turing Award, in 1982.


Fact: Scientific computing is dominated by Fortran.


Some may argue SciPy is taking hold, but fact is that scientists/engineers still use Windows, and Python on Windows isn't quite the same experience.  This is a shift I would like to see in some capacity, but Fortran and it's neolithic demeanor is here to stay. If C++ is a hammer, Fortran is a dull double-edged sword sans hilt-- it can will bludgeon you to death while cutting off your thumbs (and it won't tell you it's cutting anything off).


Some may ask, if Fortran is so prevalent, why is it almost always brought up only in context of 100-level college courses and (almost) always associated with great disgust?  The code is old and I believe people have not bothered to put in the effort to port over the code to a fast, strongly-typed compiled language (none of which are Python characteristics!) like C++.  Lots of legacy is written in Fortran 77, which features goto and if statements everywhere. No objects. An example is the ODEPACK written Fortran 77, probably on punch-cards originally. 

Fun-fact: i was able to get this code to write an 8 GB file in under 15 seconds, so perhaps the 77 designation is not as dated as you think :)

Despite this, we can use a modern framework (.NET) to call legacy Fortran code via a compiled dll. This way, we can have the versatility of the modern world, and the speed and reliability of legacy.

1. Compile the Fortran dll



The generalized version of this is

gfortran -c myfile1.f90
gfortran -c myfile2.f90
then

gfortran -shared -o -mydll.dll myfile1.o myfile2.o


2. Create a .NET project in Visual Studio.  If you are on a 64-bit computer, make sure you set the build configuration to x64 as the dll created cannot be called from a 32 bit version of your .NET program



3. Examine your Fortran file 

In this example, I show a module mod_constants that define .NET doubles (they are noted as REAL in Fortran). This is very important because .NET doubles are 64 bit, but in Fortran, they are 32 bit by default.  The selected_real_kind() modifies the precision and can create what I believe is a pseudo-64 (or 128) bit double.  However, when you assess sizeof(DP), you get 32 bits.  I am not a computer scientist so I could not attempt explain more without fudging facts.

What is important however, is that DP is set to 8 so that the size of the double annotated with "DP" is of 64 bit size.  This matches with .NET so we can call it from C# or F#.  Let me repeat, this is EXTREMELY important.



4. Open the .dll file in a text editor and find the name-mangled function.  In this case, the two functions that are used are named


__mod_funcs_MOD_mins2secs

and

__mod_funcs_MOD_secs2mins



5. Now, we will use the DllImport attribute in the System.Runtime.InteropServices namespace to give a function reference to this these embedded within the dll.  




There are a few important things here.
  1. CallingConvention.Cdecl.  This has the function caller clean the stack, allowing methods with variable number of arguments to be called.
  2. The extern keyword, since this function is external
  3. The function name is exactly as appears in the .dll when you opened it in a text editor
  4. The arguments are passed in by reference.  Fortran does what is equivalent to "passing by reference" in C#.

6. Make sure the dll is in the same directory as the .exe.


This is done by default.  If you have set up a working directory for your project, make sure the dll is referenced there.  The program does need to access the dll after all..

7. You are ready to use your function!




Now how do you do the same in F#?  Going back to step 5...


5. Declare the function in a module and give the DllImport attribute.




Please remember the 4 important things in the C# step 5, they are the same here.  Instead of the ref keyword, we use the & operator in F#

6. Make sure the dll is in the same director as the .exe


7. Ready to use the function!




One caveat here is a functional vs object-oriented (yes Fortran 2003+ is object oriented!).  If you are going to pass in a value by reference, it needs to be mutable. The functional paradigm is no mutable data. We have no choice here though, so we have to wrap the dll method in an F# function to access it.


Going forward

I hope this helped you and provided some insight on calling Fortran from .NET.  It's a fascinating concept that this can work, and it does very well.  Going further, I will post about more complex types and language interop, however this should be sufficient to get you started with real legacy Fortran code.

Comments (2) -

  • yukitos

    7/26/2014 12:16:13 AM | Reply

    I believe that we can use dumpbin.exe to detect the names of fortran functions from our dlls.

    1. Run 'Developer Command Prompt for VS2013' (or for VS2012)
    2. Run the following command:
    > dumpbin.exe /nologo /exports funcs.dll

    This result seems to be the followings:
    ----------------------------------------
    Dump of file funcs.dll

    File Type: DLL

      Section contains the following exports for funcs.dll

        00000000 characteristics
        53D2F08A time date stamp Sat Jul 26 09:04:26 2014
            0.00 version
               1 ordinal base
               3 number of functions
               3 number of names

        ordinal hint RVA      name

              1    0 0000148D __mod_funcs_MOD_mins2secs
              2    1 00001450 __mod_funcs_MOD_secs2mins
              3    2 000015BE main

      Summary

            1000 .CRT
            1000 .bss
            1000 .data
            (snip)
    ----------------------------------------

  • Bernard Hsu

    7/26/2014 6:13:47 AM | Reply

    Thanks.  This is a much better solution that taking a text editor.  I have been trying to get this to work in mono, but having trouble with x86 vs x64.  For OSX, i have been using the text editor method since no dumpbin but I know there are alternatives available.

Loading