Jerry Greenough
Aspects of Today's Fortran
The Fortran language has advanced steadily since the days of Fortran IV. In fact, many of those who were active in Fortran programming during the 1970's have difficulty in even recognizing today's constructs as Fortran code. The development of the Fortran language has been driven by the need to (i) incorporate modern programming paradigms that encourage the generation of robust and readable source code and (ii) meet the needs of high performance computing - and yet still provide a language that is simple to learn, and moreover a language which generally engenders fast and efficient computation.
Fortran 90 brought about some important enhancements to the Fortran language. This included the ability to reserve non-stack memory through the use of dynamic arrays as well as a means to encapsulate functionality by means of a 'module'. Powerful processing of array sections was introduced along with constructs for handling procedure interfaces. It was also at this stage that the programmer was given the option to write in free form, i.e. without limiting the placement of Fortran keywords to columns 7 through 72.
Fortran 2003 heralded the advent of formal constructs for object-oriented programming in Fortran. Procedure pointers were also introduced as well as techniques for interoperability with C. A standard procedure for interfacing Fortran and C reduces the number of compiler directives required for multi-platform and/or multi-compiler source code.
Today's Fortran offers the programmer many surprising capabilities that would have been challenging, if not impossible to implement in Fortran 77. Here are some (non-exhaustive) snippets/ideas that illustrate the possibilities of the language in its modern form.
• _. Operator Overloading
• _. Type-bound Procedures
This demonstrates a simple programmer-defined cross product operator written in Fortran 95.
The cross (or vector) product takes as inputs two vectors of dimension 3, $ \boldsymbol{v_1} $ and
$ \boldsymbol{v_2} $ and outputs
another vector of dimension 3, $ \boldsymbol{v_3} $ according to the familiar formula:
$$ \boldsymbol{v_3} = \boldsymbol{v_1} \times \boldsymbol{v_2} =
\begin{vmatrix} \boldsymbol{i}&\boldsymbol{j}&\boldsymbol{k} \\ v_{1x}&v_{1y}&v_{1z} \\ v_{2x}&v_{2y}&v_{2z} \end{vmatrix}
$$
Note that the above formula is implemented by the function CrossProductR3
defined inside
the module TYPES
, whose definition is listed to the right.
The cross product operator defined here provides an example of operator overloading. It
also shows that the operator token (in this case .cross.
) need not be one of the traditional
numeric or relational operators such as '*' or ' .and.
'. In fact, it can be any
combination of up to 31 letters (delimited by a period '.'), with the exception of a couple
of commonly used constants.
The operator itself is defined inside a module with an interface block. In this instance, the
purpose of the interface block is to associate the calculation function, CrossProductR3
with
the operator token .cross.
.
The .cross.
operator is easy to use, is very readable and is intuitively close to the semantics
of vector operators in mathematics.
USE TYPES
REAL(KIND=dp), DIMENSION(3) :: V1, V2, V3
V1 = (/ 1.0_dp, 0.0_dp, 0.0_dp /)
V2 = (/ 0.0_dp, 1.0_dp, 0.0_dp /)
V3 = V1 .cross. V2
Back to Aspects of Today's Fortran
A type-bound procedure can be thought of as analgous to the member function of a C++ class. There are in fact devices in Fortran 2003 for overriding the type-bound procedures of a base type with a type-bound procedure in an extended type, in much the same way that an object of a derived class might have a member function that overrides a member function of the base class.
Here is a simple example of a type-bound procedure. An Ellipse
derived type is defined inside
the module EllipseDef
(see right). The Ellipse
type defines components
for a name, a color, the resolution to be used for rendering and minor- and major- axis lengths. The objective here is to
define a type-bound procedure getName()
that will return an ellipse's name without having to directly interface
with the Ellipse
derived type's components.
The getName()
function itself is defined as a module procedure underneath the second CONTAINS
keyword. Fortran 2003 assumes (by default) that the first argument passed to a type-bound procedure is an instance of the derived type itself. In the current
example the first argument of the getName()
function is THIS
, which contains the
information about the ellipse for which the getName()
function is being invoked.
The PROCEDURE
keyword (underneath the first CONTAINS
keyword)
is there to bind the module's getName()
function to the
Ellipse
type. The following code snippet shows the 'construction' of an Ellipse
record and subsequent use of getName()
to retrieve the
name of the ellipse ELL1
using semantics enabled by Fortran 2003.
USE EllipseDef
IMPLICIT NONE
TYPE(Ellipse) ELL1
ELL1 = Ellipse('Ellipse 1',2, 20, 1.0, 2.0)
WRITE(*,*) ELL1%getName()
MODULE TYPES
INTEGER, PARAMETER :: dp = SELECTED_REAL_KIND(15, 307)
INTERFACE OPERATOR (.cross.)
MODULE PROCEDURE CrossProductR3
END INTERFACE
CONTAINS
FUNCTION CrossProductR3(V1, V2)
REAL(
Copyright © 2018 JGX Software Solutions LLC. All Rights Reserved.