Tag Archives: Fortran

Fortran

Fortran Error Handling Techniques

Error handling is critical in any software application. In computational applications, good error handling code will to detect the error as early as possible (fail fast), exit immediately, and report useful diagnostics to the user.

Boolean Return Value

One technique is to have every method return a boolean value. If the method returns true, proceed as usual, otherwise exit immediately.

function ProcessRegions() result(success)

    logical :: success

    success = .false.

    do i = 1, N

        region = regions(i)

        if (.not. ComputeDensity(region, density)) then
            return
        end if

        ! Do more stuff    

    end do

    ! No errors. Return true.
    success = .true.

end function

The downside of this technique is that you have no context as to what or why it failed. Why did the density computation fail? Was the mass zero? Did ComputeDensity method attempt to open a file that does not exist? There are 10,000 regions. Which region failed?

Without this information, you will need to debug the code interactively to figure out what went wrong.

Error Code Return Value

This is the classic programming technique of returning an integer error code. If the code is zero, by convention the method was successful. Otherwise exit immediately. The developer is responsible for making an enumeration containing error codes for all methods of failure.

integer, parameter :: ERR_None = 0, &
                      ERR_Default = 1, &
                      ERR_DivideByZero = 2, &
                      ERR_FileNotFound = 3, &
                      ERR_SomethingTerrible = 4

subroutine ProcessRegions(err) 

    integer, intent(inout) :: err ! inout since the err code should already be initialized to ERR_None.

    do i = 1, N

        region = regions(i)

        call ComputeDensity(region, density, err)
        if (err /= ERR_None) then ! Exit immediately if there's an error.
            return
        end if

        ! Do more stuff    

    end do

end subroutine

The error code now adds some context. We can now distinguish between a divide by zero error and a file not found error.

The downsides:
1) We still don’t know which region failed. This is very important if we are processing many regions.
2) We need to add an error code for every failure point in the application. For example a divide by zero error could occur in the ComputeVelocity() method. Therefore we need separate divide by zero error codes. This quickly becomes cumbersome Рwhich leads to error handling code not being written.
3) Each method call is accompanied by three extra lines of error handling boilerplate. This code will be duplicated throughout the application.

Derived Type + Macros

It’s more convenient to use a piece of text instead of a raw integer. Let’s make a derived type.

type :: ErrorType
    integer :: Code
    character(len=256) :: Message
end type

OK so now we have a richer data type to store an error message. The downside is that it is more verbose to define an error and handle an error. Next let’s add some macros to handle this.

To use macros:
1) Enable the Fortran preprocessor compiler option (/fpp). In Visual Studio use Fortran | Preprocessor | Preprocess Source File.
2) Include the file with the macros using an include statement i.e. #include 'Error.fpp'. Beware of any leading spaces in this statement.

! Error.fpp

! Macros for error handling. 
! Enables user to store errors and exit the subroutine in single statement. 
! Fortran preprocessor must be enabled: -fpp.
      
! Raise Error
! Store the error code and info (only if the current code is zero).
! Return from the subroutine.
#define RAISE_ERROR(msg, err) if (err%Code == ERR_None) then; err = ErrorType(Code=ERR_Default, Message=msg); end if; return;

! Pass Error
! Returns if there's an error.
#define HANDLE_ERROR(err) if (err%Code /= ERR_None) then; return; end if;

These macros eliminate some of the boiler plate error handling code. The RAISE_ERROR macros throws and stores the error and the HANDLE_ERROR checks the error code and exits the subroutine as necessary.

Now the full example looks like:

#include 'Error.fpp'

module Processing

    use Errors ! Error type definition and constants.
    implicit none

contains

    subroutine ProcessRegions(regions, err) 

        integer, intent(inout) :: err

        do i = 1, N

            region = regions(i)

            call ComputeDensity(region, density, err)
            HANDLE_ERROR(err)

            ! Do more stuff    

        end do

    end subroutine

    subroutine ComputeDensity(region, density, err)

        type(RegionType), intent(in) :: region
        real(8), intent(out) :: density
        type(ErrorType), intent(inout) :: err

        if (region%Volume <= 0.0) 
            RAISE_ERROR("Volume must be greater than zero. Region = " // trim(region%Name), err)
        end if

        density = region%Mass / region%Volume

    end subroutine

end module

The error message is now very descriptive. Raising an error and handling an error only takes a single line of code.

Global vs. Local Error State

The above examples store the error state using a local variable. It is tempting to use a global variable for the error state so you don’t have to pass it around as a parameter. However, using a local variable is the best practice.
1) Any subroutine that can fail will have an error variable in the interface. This helps ensure that the caller will remember to handle the error.
2) Using local variables ensures that the application can be safely executed in multi-threaded context. This is an important consideration if you ever want to run your application in parallel.

How to Create Fortran Static Libraries With Intel Fortran and Visual Studio

Intel Fortran with Visual Studio integration makes it easy to create and consume static libraries. Static libraries are shared components that compile to LIB files which in turn link into DLLs and EXEs.

Static libraries have several benefits:

  • Unit Testing Static libraries enable unit testing. The static library makes it easy to create a unit test project that tests the static library in isolation. The unit test project is typically a console application. This pattern is also common in C++.
  • Internal Code Reuse Code that is shared among multiple DLLs within a solution can be collocated in a static library.
  • External Code Reuse Static libraries can also be used to publish code for use by 3rd parties. This use case is more complicated since certain compiler options can cause binary incompatibilities such as the choice run-time library. This use case is not covered in this post.
  • Organization Splitting up a large project into several static libraries makes the application easier to understand and maintain.

The the example below shows how to compile a static libraries into a Fortran console application.

Solution Setup

The solution has two projects: the “Calculator” static library and the main console application that calls the Calculator.

fsl-solution_explorer

Static Library

The static library has a single module with some advanced numerical computation routines. The Subtract subroutine has the bind(c) attribute. This will affect how the compiler exports the name of the function in the LIB.

module Calculator

    implicit none

contains

    function Add(x, y) result(z)

        real(8) :: z
        real(8), intent(in) :: x, y

        z = x + y

    end function

    function Subtract(x, y) result(z) bind(c)

        real(8) :: z
        real(8), intent(in) :: x, y

        z = x - y

    end function

end module

After you compile the LIB file, you can use DUMPBIN to inspect how the functions are being exposed. This is important when using LIB files from 3rd parties. DUMPBIN is a command line utility for inspecting binaries. The easiest way to run DUMPBIN is to open the Visual Studio command prompt from the Start Menu (or a shortcut) and cd to the target directory.

fsl-vs_cmd

(If the project does not compile a LIB file, try putting a module-less subroutine that calls a library routine. I’ve occasionally seen this behavior when starting a new project. Once the LIB compiles, you can get rid of the dummy subroutine.)

For this application use dumpbin /symbols to show the exported symbols. The Add and Subtract symbols are highlighted in the figure below. The Add method is exported as _CALCULATOR_mp_ADD where mp = ‘module procedure’. This is the typical Fortran name mangling convention. The Subtract method is exported using the C convention, _subtract since bind(c) was used. This means that the Subtract method can be linked into a C or C++ application.

fsl-dumpbin

Link the Static Library

Linking the static library to the calling application is very easy using the Visual Studio integration.

First, add the calling application project. In this sample we use a console application shown below. The Main subroutine references the Calculator module from the static library.

program Main

    use Calculator

    implicit none

    real(8) :: x, y

    x = 1.0
    y = 2.0

    print *, 'Add', Add(x, y)
    print *, 'Sub', Subtract(x, y)

end program

To link the static library, right-click on the calling project and select Project Dependencies. Select “Calculator” as a dependency of the “Program” application and click OK. Build the solution, and your are done.

fsl-project_dependencies

When interfacing with 3rd party LIB files (w/out source code), you will need to point the compiler to the path of the LIB files using the following linker options. If the static library uses modules, you will also need to point the compiler to the module (*.mod) binaries. Things get more complicated when dealing with Debug and Release modes and 32- and 64-bit configurations.

  • Fortran | General | Additional Include Directories (for modules)
  • Linker | General | Additional Library Directories
  • Linker | Input | Additional Dependencies

But if you have access to the source code to compile the LIB internally, the Visual Studio integration takes care of all this.

Sample Code

The link below contains sample code using Visual Studio 2010 and Intel Fortran.

Sample-StaticLib.zip

How to Call Fortran DLLs from C#

C# has direct support for calling Fortran via DLLs using P/Invoke. In C#, simply define a static method with the same method signature as the Fortran method. No code generation tools are needed.

Using Fortran with a high-level language like C# allows you to leverage the strengths of both languages. C# can be used to create a GUI as well as preprocess data from files, database, or other applications. This leaves Fortran to focus solely on the number crunching.

Here are some general rules for calling Fortran from C#.

  • The Fortran DLL should reside in the same directory as the C# assembly (DLL or EXE). Automatically copy the Fortran DLL using post-build events.
  • Array indices are switched between Fortran and C#. Fortran and C# arrays are stored column and row major order respectively.
  • Fortran derived types map to C# structs. Be sure to explicitly define the alignment. The default alignment in Intel Fortran is 8 bytes.
  • Scalar values in Fortran are passed by reference. The equivalent C# scalar should be passed by reference (ref) as well. Failing to do so will usually cause memory access violations.
  • C# strings should be converted to character arrays padded with spaces.

Solution Setup

The Visual Studio solution contains and Fortran dynamic link library project and a C# console application. In order for the C# console application to call the Fortran DLL, it must be in the same directory as the C# executable. Other configurations are possible, but are not recommended due to the potential for chaos (DLL hell).

Use post-build events to automatically copy the Fortran DLL to the C# build directory as shown below. Here we use some of the Visual Studio macros. Remember to include the quotes if you solution is located in a path with spaces.
copy "$(OutDir)\$(TargetName).dll" "$(SolutionDir)\CSharpApp\bin\Debug"
copy "$(OutDir)\$(TargetName).dll" "$(SolutionDir)\CSharpApp\bin\Release"

post-build_fortran

Passing Arrays

The following example shows how to pass arrays. The Fortran subroutine translates an array of coordinates by the vector delta.

coords is a 3 x n array where n is the number of coordinates. n is passed into the subroutine.

Several compiler directives are used to export the subroutine to the public interface of the DLL. The ATTRIBUTES DLLEXPORT directive exports the subroutine. The ATTRIBUTES ALIAS overrides any compiler specific name mangling. Compiler directives are prepended by !DIR$, !DEC$, or !MS$.

subroutine TranslateViaArrays(delta, n, coords)

    !DIR$ ATTRIBUTES DLLEXPORT :: TranslateViaArrays
    !DIR$ ATTRIBUTES ALIAS: 'TranslateViaArrays' :: TranslateViaArrays

    real(8), intent(in) :: delta(3)
    integer, intent(in) :: n
    real(8), intent(inout) :: coords(3,n)

    integer :: i

    do i = 1, n
        coords(:,i) = coords(:,i) + delta(:)
    end do

end subroutine

The calling code in C# wraps the DLL call in a static class call FortranLib. The name of the DLL is defined in a private field.

Within the class a static extern method is defined with the [DllImmport] attribute. The DllImport attribute defines how the DLL is called – most importantly the DLL name, calling convention, and the character set.

The signature of the method matches the Fortran version with some extra attributes to describe how each argument is marshalled from C# to Fortran and back.

  • The scalar argument n is passed by reference (ref) as this is the Fortran convention.
  • The array arguments do not use the ref keyword since arrays are reference types in C# (as opposed to scalar primitives like n which are value types).
  • The array arguments are annotated with the [In] and [Out] attributes. These should match the intent of the Fortran version.
  • The default calling convention for Intel Fortran is C (cdecl).
public static class FortranLib
{
    private const string _dllName = "FortranLib.dll";

    public const int PathLength = 256;

    [DllImport(_dllName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
    public static extern void TranslateViaArrays([In] double[] delta, ref int n, [In, Out] double[,] coords);
}

The snippet below shows the C# calling code. Notice that the array indices in the coords are reversed between C# and Fortran. The rest is standard C#.

static void RunTranslateViaArrays()
{
    var delta = new[] { 1.0, 2.0, 3.0 };
    int n = 10;
    var coords = new double[n, 3]; // Note that the indices are reversed from Fortran.
    for (int i = 0; i < n; i++)
    {
        coords[i, 0] = i;
        coords[i, 1] = i + 1;
        coords[i, 2] = -i;
    }

    FortranLib.TranslateViaArrays(delta, ref n, coords);

    for (int i = 0; i < n; i++)
        Console.WriteLine("Point {0}: ({1}, {2}, {3})", i + 1, coords[i, 0], coords[i, 1], coords[i, 2]);
}

Passing Types

The following Fortran code performs the same operation as before, except now the point coordinates are encapsulated in a derived type PointType. This derived type will map to an equivalent C# struct.

The bind(c) attribute dictates that the memory layout should be the same as in C – sequential with padding as necessary. The default padding in Intel Fortran is 8 bytes (/align:rec8byte). In PointType a padding integer is placed after the Id field so that the double precision coordinate fields start on the subsequent 8-byte boundary. You can confirm this at run-time using the inquire statement (shown below).

type, bind(c) :: PointType
    integer :: Id
    real(8) :: Coords(3)
end type

subroutine TranslateViaTypes(delta, n, points)

    !DIR$ ATTRIBUTES DLLEXPORT :: TranslateViaTypes
    !DIR$ ATTRIBUTES ALIAS: 'TranslateViaTypes' :: TranslateViaTypes

    real(8), intent(in) :: delta(3)
    integer, intent(in) :: n
    type(PointType), intent(inout), target :: points(n)

    integer :: i, words
    type(PointType), pointer :: point

    inquire(iolength=words) point
    print *, words ! Confirm the size of PointType. Id(1) + Pad(1) + Coords(3*2) = 8 words. 1 word = 4 bytes.

    do i = 1, n
        point => points(i) ! Use a local (pointer) variable to avoid writing points(i) multiple times.
        point%Coords(:) = point%Coords(:) + delta(:)
    end do    

end subroutine

The equivalent C# struct is shown in the code below:

  • The StructLayout attribute dictates that the memory layout is sequential (like bind(c)). The Pack option specifies a padding of 8 bytes.
  • The coords array has a special MarshalAs attribute which tells C# to reserve space for 3 doubles. C# does not have native support for fixed size arrays (essentially all C# arrays are like Fortran allocatable arrays). C# does have a notion of a fixed array which requires an unsafe code block which I have not tried.
  • The downside of the current approach is that if the struct is created with the default constructor (i.e. points = new Points[10]), it is in an unsafe state since the Coords field is null (high potential for NullReferenceExceptions).
  • The sample code has examples of other implementations.
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct Point
{
    public int Id;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
    public double[] Coords;

    public Point(int id, double x, double y, double z)
    {
        Id = id;
        Coords = new double[3] { x, y, z };
    }
}

The P/Invoke signature of the C# call is practically the same as the previous example. Switching from raw arrays to types helps make the code self documenting.

[DllImport(_dllName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern void TranslateViaTypes([In] double[] delta, ref int n, [In, Out] Node[] nodes);

static void RunTranslateViaTypes()
{
    var delta = new[] { 1.0, 2.0, 3.0 };
    int n = 10;
    var points = new Point[n];
    for (int i = 0; i < n; i++)
        points[i] = new Point(id: i + 1, x: i, y: i + 1, z: -i);

    FortranLib.TranslateViaTypes(delta, ref n, points);

    for (int i = 0; i < n; i++)
    {
        var point = points[i];
        Console.WriteLine("Point {0}: ({1}, {2}, {3})", point.Id, point.Coords[0], point.Coords[1], point.Coords[2]);
    }
}

Passing Strings

Passing strings to Fortran requires that you convert the C# string to a fixed length character array. The following Fortran code shows an example use case where the path to an input file is passed to the DLL subroutine.

subroutine TranslateViaInputFile(inputPath)

    !DIR$ ATTRIBUTES DLLEXPORT :: TranslateViaInputFile
    !DIR$ ATTRIBUTES ALIAS: 'TranslateViaInputFile' :: TranslateViaInputFile

    character(len=LEN_PATH), intent(in) :: inputPath

    print *, "Input File: " // trim(inputPath)

    ! Do something amazing....

end subroutine

The P/Invoke call is similar to the ones before.

[DllImport(_dllName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern void TranslateViaInputFile([In] char[] inputPath);

When converting C# strings to character arrays you must ensure that: 1) the array has the correct length and 2) leftover characters are written as whitespace. The following extension method handles this conversion.

public static char[] ToCharacterArrayFortran(this string source, int length)
{
    var chars = new char[length];
    int sourceLength = source.Length;
    for (int i = 0; i < length; i++)
    {
        if (i < sourceLength)
            chars[i] = source[i];
        else
            chars[i] = ' '; // Important that these are blank for Fortran compatibility.
    }
    return chars;
}

The calling code uses the extension method to create the character array to pass into Fortran.

static void RunTranslateViaInputFile()
{
    const string inputPath = @"C:\temp\input.txt";

    // Convert string to Fortran array of characters.
    char[] inputPathChars = inputPath.ToCharacterArrayFortran(FortranLib.PathLength);

    FortranLib.TranslateViaInputFile(inputPathChars);
}

Debugging

To debug Fortran from the C# application you must activate the unmanaged code debugging in the C# project. Project | Properties | Debug | Enable unmanaged code debugging. Once this option is enabled, you can step directly into the Fortran DLL from the C# application.

enable_unmanaged_debugging_fortran

Sample Code

The link below contains sample code using Visual Studio 2010 and Intel Fortran.
CSharpFortranInterop.zip