Linking Excel to Fortran – 2

In this post the procedures described for C++ here will be replicated for Fortran:

The spreadsheet, fortran source code, and compiled dll described in this code may be downloaded from Cubic.zip

As always, it is better to use the downloaded code, rather than copying from the post, because the WordPress software changes quotes (“”) and several other characters, so that code copied and pasted from the post will often not run without editing.

This post describes:

  • Transferring arrays between VBA and Fortran.
  • Writing and compiling a simple Fortran function to solve quadratic equations, and linking to it from Excel.
  • Compiling a Fortran function to solve cubic equations, and linking from Excel. 
  • The Fortran code for the quadratic function (QUADA) is shown below:


    F_stdcall FUNCTION QUADA(qdat, ResA, NumRows)
    IMPLICIT NONE

    INTEGER, intent(in) :: NumRows
    REAL(2), intent(in) :: qdat(NumRows, 3)
    REAL(2), intent(out) :: ResA(NumRows,2)

    REAL(2) :: QUADA
    REAL(2) :: P, DIS

    INTEGER I

    DO I=1, NumRows

    IF (qdat(I,1).NE.0.D+0) THEN

    ! quadratic problem
    P=5.D-1*qdat(I,2)/qdat(I,1)
    DIS=P**2-qdat(I,3)/qdat(I,1)
    IF (DIS.GE.0.D+0) THEN
    ! two real solutions!
    ResA(I,1)=-P-SQRT(DIS)
    Resa(I,2)=-P+SQRT(DIS)
    QUADA=2
    ELSE
    ! no real solution!
    QUADA=0
    END IF

    ELSE IF (qdat(I,2).NE.0.D+0) THEN

    ! linear equation
    ResA(I,1)=-qdat(I,3)/qdat(I,2)
    QUADA=1

    ELSE
    ! no equation
    QUADA=0
    END IF
    END DO

    RETURN
    END FUNCTION QUADA

    Note that:

    • The function is preceded with “F_stdcall”
    • The function parameters: qdat, ResA, and NumRows are declared as either intent(in) or intent(out); intent(inout) is also allowed.
    • The arrays qdat and ResA are dynamic, and will be sized at run time to the size specified in NumRows
    • Paramaters that are specified as double in VBA are REAL(2) in Fortran
    • Parameters specified as Long in VBA are Integers in Fortran
    • It is essential to ensure that both the data types and the sizes of arrays in the VBA and Fortran routines match.

    The fortran routine is compiled as a dll as before, and may then be called from a VBA routine.  The dll has been named cubic.dll, since it contains both the quadratic and cubic functions:


    Declare Function QUADA Lib "D:\Users\...\Cubic\Release\Win32\Cubic.dll" (qdat As Double, ResA As Double, NumRows As Long) As Double

    Function FQuada(QuadData As Variant) As Variant
    Dim xa() As Double
    Dim A As Double, b As Double, c As Double
    Dim ResA() As Double, NumRows As Long
    Dim Retn As Double, i As Long, j As Long
    On Error Resume Next

    QuadData = QuadData.Value2
    NumRows = UBound(QuadData) – LBound(QuadData) + 1

    ReDim xa(1 To NumRows, 1 To 3)
    ReDim ResA(1 To NumRows, 1 To 2)
    For i = 1 To NumRows
    For j = 1 To 3
    xa(i, j) = QuadData(i, j)
    Next j
    Next i
    Retn = QUADA(xa(1, 1), ResA(1, 1), NumRows)

    FQuada = ResA

    End Function

    As for C++ dlls, array parameters are passed by reference by entering the first member of the array. Unlike C++, in Fortran the array members are named in the same way as VBA; i.e. ArrayName(Row No, Column No).

    A screen shot showing the output of the quadratic function is shown below:

    Output from QuadA

    Output from QuadA

    Fortran code for two Cubic functions, and the resulting dll, are included in the download files.  The code to call these functions from VBA is shown below:
    Declare Function CUBIC Lib “D:\Users\…\Cubic\Release\Win32\Cubic.dll” (cdat As Double, ResA As Double) As Double
    Declare Function CUBICA Lib “D:\Users\..,\Cubic\Release\Win32\Cubic.dll” (cdat As Double, ResA As Double, NumRows As Long) As Double

    Function FCUBIC(P As Variant) As Variant
    Dim P1(1 To 4) As Double, Res(1 To 1, 1 To 3) As Double
    Dim cval As Double, i As Long

    P = P.Value

    For i = 1 To 4
    P1(i) = P(1, i)
    Next i

    cval = CUBIC(P1(1), Res(1, 1))

    FCUBIC = Res

    End Function

    Function FCubica(CubicData As Variant) As Variant
    Dim xa(1 To 4) As Double
    Dim ResA1() As Double, ResA(1 To 1, 1 To 3) As Double, NumRows As Long
    Dim Retn As Double, i As Long, j As Long
    On Error Resume Next

    CubicData = CubicData.Value2
    NumRows = UBound(CubicData) – LBound(CubicData) + 1

    ReDim ResA1(1 To NumRows, 1 To 3)
    For i = 1 To NumRows
    For j = 1 To 4
    xa(j) = CubicData(i, j)
    Next j
    Retn = CUBIC(xa(1), ResA(1, 1))
    For j = 1 To 3
    ResA1(i, j) = ResA(1, j)
    Next j
    Next i

    FCubica = ResA1

    End Function

    Function FCubica2(CubicData As Variant) As Variant
    Dim xa() As Double
    Dim ResA() As Double, NumRows As Long
    Dim Retn As Long, i As Long, j As Long
    On Error Resume Next

    CubicData = CubicData.Value2
    NumRows = UBound(CubicData) – LBound(CubicData) + 1
    ReDim xa(1 To NumRows, 1 To 4)
    ReDim ResA(1 To NumRows, 1 To 3)
    For i = 1 To NumRows
    For j = 1 To 4
    xa(i, j) = CubicData(i, j)
    Next j
    Next i
    Retn = CUBICA(xa(1, 1), ResA(1, 1), NumRows)

    FCubica2 = ResA

    End Function

    The code includes one function that operates on one equation at a time, and two alternative array functions that provide much better performance by avoiding the transfer of data between the spreadsheet and VBA. For comparison VBA cubic functions are also included in the download file.

    Screenshot of cubic function output and relative performance results:

    cubic

    Posted in Excel, Fortran, Link to dll, Maths, UDFs, Uncategorized, VBA | Tagged , , , , , | 23 Comments

    Linking Excel to Fortran

    An earlier series of posts described how to link Excel to programs written in C or C++.  The same advantages of increased security and performance may be obtained by linking to Fortran programs with the additional advantages:

    • Many specialist engineering and scientific programs are only available in Fortran
    • The structure and syntax of Visual Basic is closer to Fortran than C
    • Fortran has features for the manipulation of arrays that are particulary useful for engineering and scientific analysis.
    • The linking process is much simpler

    This post will look at creating four simple maths functions in Fortran, and linking them so that they are available in Excel, either to VBA routines, or directly from the spreadsheet.  Later posts will look at creating more complex and useful programs, including passing arrays, and some catches in the handling of floating point numbers that need to be watched for.

    The examples will all be using Silverfrost Personal Fortran, which is available for download, and is free for non-commercial use.

    To create MathFunc.dll 

    1.  Create a new directory called MathFunc at any convenient location
    2.  Start up the Plato IDE (the Silverfrost Fortran editor)
    3.  Create a new dll project called MathFunc:

    Select File – New Project
    Select Fortran DLL
    Enter MathFunc for the name
    Browse for the MathFunc directory that you previously created

    Craete new Fortran DLL Project

    Craete new Fortran DLL Project

    4. Create a new file in the project: Project – Add New Item; select Free Format Fortran file, and enter MathFunc.f95 for the name.

    5. Enter or copy and paste the code shown below:


    ! MathFuncs
    F_stdcall function ADD(a, b)
    REAL(2), intent(in) :: a, b
    REAL(2) ADD
    ADD = a + b
    RETURN
    end function ADD

    F_stdcall function SUBTRACT(a, b)
    REAL(2), intent(in) :: a, b
    REAL(2) SUBTRACT

    SUBTRACT = a - b
    RETURN
    end function SUBTRACT

    F_stdcall function MULTIPLY(a, b)
    REAL(2), intent(in) :: a, b
    REAL(2) MULTIPLY

    MULTIPLY = a * b
    RETURN
    end function MULTIPLY

    F_stdcall function DIVIDE(a, b)
    REAL(2), intent(in) :: a, b
    REAL(2) DIVIDE

    IF(b == 0) THEN
    DIVIDE = 0
    ELSE
    DIVIDE = A/B
    END IF
    RETURN
    end function DIVIDE

    6. Select Build – Build, or click the Build icon.
    7. That’s all.

    The program will create a file called MathFunc.dll in a subdirectory: \CheckMate\Win32\.  When debugging is complete you can select “Release Win32” from the drop-down list at the top of the editor screen, and a release version of the dll will be created in \Release\Win32\.

    To make the four functions in MathFunc.dll available in Excel:

    1.  Open a new workbook and save it as MathFunc.xls
    2.  Open the Visual Basic Editor (press Alt-F11), insert a new module, and enter the following lines of code:


    Declare Function ADD Lib "D:\Users\...\MathFunc\CheckMate\Win32\poly.dll" (A As Double, b As Double) As Double
    Declare Function SUBTRACT Lib "D:\Users\...\MathFunc\CheckMate\Win32\poly.dll" (A As Double, b As Double) As Double
    Declare Function MULTIPLY Lib "D:\Users\...\MathFunc\CheckMate\Win32\poly.dll" (A As Double, b As Double) As Double
    Declare Function DIVIDE Lib "D:\Users\...\MathFunc\CheckMate\Win32\poly.dll" (A As Double, b As Double) As Double

    … adjusting the file path to the full path name for your file locations.

    The four new MathFunc should now be available in the worksheet, just like the built in Excel functions:

    MathFunc functions called from spreadsheet

    MathFunc functions called from spreadsheet

    The Fortran files and Excel spreadsheet described in this post may be downloaded from: MathFunc.zip

    Posted in Excel, Link to dll, VBA | Tagged , , | 20 Comments

    Silverfrost Personal Fortran

    Silverfrost Personal Fortran is a Fortran95 compiler that is free for personal use.  According to the suppliers:

    “With Silverfrost FTN95: Fortran for Windows you can create console based, conventional Windows® and Microsoft .NET applications. Silverfrost FTN95 is the only compiler that can produce Microsoft .NET applications that can use the full Fortran 95 language. Silverfrost FTN95 also boasts its world leading CHECKMATE run-time checking technology that finds bugs fast!

    Silverfrost FTN95: Fortran for Windows is now available free for personal and evaluation use. To try the compiler click here.”

    What does this have to do with Excel?

    My next post will deal with linking Excel to Fortran DLL’s, using the Silverfrost compiler.

    Posted in Computing - general, Excel, Fortran, Link to dll, Newton, VBA | Tagged , , , | 1 Comment

    Finding Prime Numbers with Excel, and using Array Formulas

    Download Primes.zip.

    Several of the puzzles at Project Euler (see previous post) require the finding of prime numbers, for instance what is the 10,001th prime number?  Searching the Web for some ready made solutions I found this array formula at Chip Pearson’s site:

    =IF(OR(C8=2,C8=3),”prime”,IF(AND((MOD(C8,ROW(INDIRECT(“2:”&INT(SQRT(C8)))))<>0)),”prime”,”not prime”)) *

    * This formula is slightly amended from the one on Chip’s site, which treated 1 as a prime, and divided by all numbers up to C8-1, rather than SQRT(C8)

    Quoting from Chip’s site:

    “This is an array formula, so you must press CTRL SHIFT ENTER rather than just ENTER when you first enter the formula and whenever you edit it later. If you do this properly, Excel will display the formula enclosed in curly braces { }. See the array formulas pagefor much more detail about array formulas. Note that the formula will not work properly if you do not enter it with CTRL SHIFT ENTER. This formula will work with numbers up to 268,435,455, after which Excel’s ability to handle the intermediate array fails. “

    Now it may not be immediately obvious to you how that formula works, certainly it was not at all obvious to me,so let’s break it down into small chunks:

    Breakdown of an array formula

    Breakdown of an array formula

    Entering 25 in cell C8:
    =”2:”&INT(SQRT(C8)) returns 2:5

    =ROW(INDIRECT(“2:”&INT(SQRT(C8)))) returns a column array with the numbers {2;3;4,5}

    =MOD(C8,ROW(INDIRECT(“2:”&INT(SQRT(C8))))) returns a column array with the remainder of 25 divided by the members of the array above {1;1;1,0}

    =AND((MOD(C8,ROW(INDIRECT(“2:”&INT(SQRT(C8)))))<>0)) returns TRUE if all the remainders are greater than zero, or FALSE if there are one or more zeroes, and finally

    =IF(AND((MOD(C8,ROW(INDIRECT(“2:”&INT(SQRT(C8)))))<>0)),”prime”,”not prime”) returns “prime” if the statement above is TRUE, or “not prime” if it is FALSE.

    The initial: =IF(OR(C8=2,C8=3),”prime”,… is required because the main formula does not work for values less than 4, so it is necessary to check for entered values of 2 or 3 (but not 1, which is not defined as a prime number).

    The reason why this formula must be entered as an array formula is that it is necessary to check for all the divisions in the array generated by the ROW statement. If the formula is enetered without pressing “Ctrl-Shift-Enter” the AND function only checks the first member of the array, and finds that all odd numbers are prime numbers.

    This is an excellent example of the power of an array formula, allowing complex multi-stage computations to be carried out in a single cell; it is however not of much use for Project Euler purposes, which required finding the 10,001st prime for instance. For that purpose I have written my own prime UDF – download here.

    A UDF to generate a sequence of primes

    A UDF to generate a sequence of primes

    =Prime(MaxVal, First, Last)

    This function will find all the prime numbers in a given range (e.g. from the 99,995th to 100,001st prime as shown above).  If “First” is not entered or is zero the function checks if MaxVal is prime.

    For checking if one number is prime the UDF uses a similar approach to the array function, except that it only checks odd numbers, since we know that even numbers other than 2 cannot be prime.

    For generating a range of primes it uses the “Sieve of Eratosthenes” method:

    • Set up an array from 1 to MaxVal
    • Enter a 1 for each number in the array divisible by 2 or 3
    • For each odd number from 5 to MaxVal:
    • If its array value = 1, it is not prime, check the next odd number
    • If its array value = 0, it is prime, add it to the prime list and enter 1 for each number in the array that is an odd multiple of this prime number.
    • Stop when the last prime in the required sequence has been found, or MaxVal has been reached.

    More refinements on the Sieve of Eratosthenes method are given here: Fun With Prime Numbers

    Posted in Arrays, Excel, Maths, Newton, UDFs, VBA | Tagged , , , , | 28 Comments

    Project Euler

    Whilst tag-surfing I discovered Project Euler, which looks like fun for people who enjoy mathematical puzzles.  Here’s what they say about themselves:

    What is Project Euler?

    Project Euler is a series of challenging mathematical/computer programming problems that will require more than just mathematical insights to solve. Although mathematics will help you arrive at elegant and efficient methods, the use of a computer and programming skills will be required to solve most problems.

    The motivation for starting Project Euler, and its continuation, is to provide a platform for the inquiring mind to delve into unfamiliar areas and learn new concepts in a fun and recreational context.”

    After you register and log in you can post an answer to the questions, and they will tell you if it is right or not.

    Posted in Maths, Newton | Tagged , | Leave a comment