Fortran

From PHASTA Wiki
Revision as of 17:21, 4 February 2021 by Jrwrigh (talk | contribs) (Replace equivalence statement with associate statement documentation)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Fortran (short for Formula Translation) is a programming language geared towards numerical computation.

Documentation

Some documentation of the language format can be found at Intel's Developer Guide.

Fortran subroutines can also be documented using Doxygen. It is recommended that you document your code in a Doxygen format, as we're looking to move to Doxygen for PHASTA documentation.

Miscellaneous Notes

Associate Statement

Associate is effectively the replacement for (and better version of) the equivalence statement (which was formerly documented here, removed to reduce confusion). Effectively, it adds clarity to programming by allowing you to name "temporary variables"

The associate statement allows different variable names to reference the same data storage (ie. memory allocation). A common paradigm in PHASTA is the use of temporary arrays to store the partial result of calculations that maybe reused multiple times. For example, the function below calculates sum over i of [ (sqrt(a) * sqrt(b))^2_i + sqrt(a * b)_i ], where a and b are vectors:

function example_func1(a, b) result(c)
    implicit none
    real, intent(in), dimension(4) :: a, b
    real, intent(out) :: c
    real, dimension(4) :: temp

    temp = a*b
    c = sqrt(temp)

    temp = sqrt(a)*sqrt(b)
    c = c + temp**2

    return
end function example_func1

While reusing the temp array for both a*b and sqrt(a)*sqrt(b) is memory efficient (as we only need to have 2 1x4 arrays, temp and c) and computationally efficient (as we calculate a*b only once), it's not very easy to read as the definition of temp changes throughout the code.

To improve the legibility, we could set a*b and sqrt(a)*sqrt(b) to be different variables instead:

function example_func2(a, b) result(c)
    implicit none
    real, intent(in), dimension(4) :: a, b
    real, intent(out) :: c
    real, dimension(4) :: ab, ab_sqrt

    ab = a*b
    c = sqrt(ab)

    ab_sqrt = sqrt(a)*sqrt(b)
    c = c + ab_sqrt**2

    return
end function example_func2

This is easier to read, as the contents of ab and ab_sqrt are far less ambiguous. However, note that we are less memory efficient than before; we now require 3 1x4 arrays (ab, ab_sqrt, and c) compared to the previous 2. This may not seem like much, but memory speed is often the bottle-neck when it comes to HPC performance, so a 50% increase in required memory is significant. This is especially true when you deal with arrays with millions of elements rather than just 4.

However, using the associate statement, we can get the best of both worlds:

function example_func3(a, b) result(c)
    implicit none
    real, intent(in), dimension(4) :: a, b
    real, intent(out) :: c
    real, dimension(4) :: temp

    associate(ab => temp)
        ab = a*b
        c = sqrt(ab)
    end associate

    associate(ab_sqrt => temp)
        ab_sqrt = sqrt(a)*sqrt(b)
        c = c + ab_sqrt**2
    end associate

    return
end function example_func3

Here, ab and ab_sqrt use the same memory, thus we only require 2 1x4 arrays. example_func3 is compiled as completely identical to example_func1, but it's more easily readable. It also has the nice effect of separating the code into computation chunks; if you use temporary variables, each new associate block represents a new "task" being completed.