Fortran modules

Modules provide a way to package together groups of functions, subroutines, and data that all serve some common purpose. Splitting your code into well thought out modules is probably the most useful thing you can do for readability and maintainability.

Modules can also import the contents of other modules. This allows more complex functionality to be methodically built up from more fundamental pieces.

The general structure of a Fortran module

module <MODULE-NAME>
   ! Import any other modules

   ! Declare variables

contains

   ! Define subroutines or functions
end module <MODULE-NAME>

A program or subroutine can use this module like so:

program <NAME>
   use <MODULE-NAME>

   ! Declare program variables

   ! Execute statements, call the module, etc.
end program <NAME>

It is useful to make your module imports more explicit. The use statement can be replaced by

use <MODULE-NAME>, only: <LIST OF SYMBOLS>

to specify that only certain variables/subroutines/functions from the module should be used. Doing it this way also makes it clear exactly what symbols are coming from which module in the case where you use several modules.

A very simple module is:

 1! /codes/sub1m.f90
 2
 3module sub1m
 4
 5contains
 6
 7  subroutine sub1()
 8    print *, "In sub1"
 9  end subroutine sub1
10
11end module sub1m

Download this code

and a program that uses it is:

1! /codes/main.f90
2
3program demo
4  use sub1m, only: sub1
5  print *, "In main program"
6  call sub1()
7end program demo

Download this code

These source files can be compiled as:

gfortran -c sub1m.f90
gfortran -c main.f90
gfortran -o modules.ex main.o sub1m.o

Some reasons to use modules

  • Can avoid global variables by putting shared resources into a module, and having multiple portions of the code reference that module. In Fortran 77 this had to be done with common blocks – much less elegant.

  • Subroutine/function interface information is generated to aid in checking that proper arguments are passed. It’s often best to put all subroutines and functions in modules for this reason.

  • Can define new data types to be used in several routines.

Compiling modules

Modules must be compiled before any program units that use the module. When a module is compiled, a .o file is created, but also a .mod file is created that must be present in order to compile a unit that uses the module. This .mod file contains, among other information, the generated interface information.

Circles module example

Here is an example of a module that defines one parameter pi and two functions:

 1! /codes/circle1/circle_mod.f90
 2
 3module circle_mod
 4
 5  implicit none
 6  integer, parameter :: fp = selected_real_kind(15)
 7  real (fp), parameter :: pi = 3.141592653589793d0
 8
 9contains
10
11  real (fp) function area(r)
12    real (fp), intent(in) :: r
13    area = pi * r**2
14  end function area
15
16  real (fp) function circumference(r)
17    real (fp), intent(in) :: r
18    circumference = 2.d0 * pi * r
19  end function circumference
20
21end module circle_mod

Download this code

This might be used as follows:

 1! /codes/circle1/circle_main.f90
 2
 3program circle_main
 4
 5  use circle_mod, only: pi, area, fp
 6  implicit none
 7  real (fp) :: a
 8
 9  ! print parameter pi defined in module:
10  print *, 'pi = ', pi
11
12  ! test the area function from module:
13  a = area(2.0_fp)
14  print *, 'area for a circle of radius 2: ', a
15
16end program circle_main

Download this code

To compile, you first need to generate circle_mod.o and circle_mod.mod, followed by circle_main.o. Only then, you can link the two object files together to produce a binary (or an executable) file, e.g., circle.ex

$ gfortran -c circle_mod.f90     # produces circle_mod.o and circle_mod.mod
$ gfortran -c circle_main.f90    # produces circle_main.o
$ gfortran -o circle.ex circle_main.o circle_mod.o # linking to generate circle_main.ex

If you reverse the order of the compilations between circle_mod.f90 and circle_main.f90 in the above it won’t compile and fail with an error message such as

circle_main.f90:5.6:

use circle_mod, only: pi, area
      1
Fatal Error: Can't open module file 'circle_mod.mod' for reading at (1): No such file or directory

Executing the program by running ./circle.ex after a successful compilation gives the following output

pi =    3.14159265358979
area for a circle of radius 2:    12.5663706143592

Note that a parameter defined with a specific value in a module (e.g., pi as defined in circle_mod.f90) is available to all program units using the module (e.g., pi is accessible from circle_main.f90 via use circle_mod, only: pi). See Module variables and parameters.

Note

You can now see that compiling \(N\) source files via using \(N\) calls to gfortran will be very tedious, especially when \(N\) is large. In this case compiling with a Makefile becomes very handy (see Makefiles).

Module variables and parameters

It is also possible to declare variables within a module that can be shared between all program units using the module. This alone is essentially the same as using global variables, which we would like to avoid. We’ll see shortly how to avoid the shortcomings of this style, and ultimately find module variables to be extremely useful.

Module variables can also be directly referenced by any subroutines and functions defined in that module without needing to pass them as arguments. This substantially cleans up the code.

If you want the value of a variable to persist between references to it you can use the save keyword. For example:

real (kind=8), save :: pi

is used to indicate that the variable pi defined in the module should have its value saved for use by any program unit with access to the module. This is a better way to manage shared resources than using global variables. However, care must be taken to avoid letting these saved module variables become global variables in disguise.

Remark Care should be taken when using this idea, as a strong reliance on saved variables stored within (other) modules can make code harder to read, understand, and debug. They still have a purpose though. I would advise using save to store state internal to the module, and avoid using those variables directly when outside the module. A common use case for this is when a module needs to perform some memory allocation internally.

Remark Placing related constants together into a module is an excellent use case for module variables. In this case each will be declared with parameter and set at the declaration site. Consult the module in utility.f90 from Fortran Example – Mathieu functions and characteristics.

Here is another version of the circles code that stores pi as a module variable rather than a parameter:

 1! /codes/circle2/circle_mod2.f90
 2! Version where pi is a module variable.
 3
 4module circle_mod2
 5
 6  implicit none
 7  integer, parameter :: fp = selected_real_kind(15)
 8  real (fp), save :: pi 
 9
10contains
11
12  subroutine initialize()
13
14    ! Set the value of pi
15    pi = acos(-1.0_fp)
16
17  end subroutine initialize
18
19  real (fp) function area(r)
20    real (fp), intent(in) :: r
21    area = pi * r**2
22  end function area
23
24  real (fp) function circumference(r)
25    real (fp), intent(in) :: r
26    circumference = 2.0_fp * pi * r
27  end function circumference
28
29end module circle_mod2

Download this code

In this case we also need to initialize the variable pi by means of a subroutine. Note the new initialize subroutine defined in this version of the module.

These might be used as follows in a main program:

 1! /codes/circle2/circle_main2.f90
 2
 3program circle_main2
 4
 5  use circle_mod2, only: fp, pi, initialize, area
 6  implicit none
 7  real (fp) :: a
 8
 9  call initialize()   ! sets pi
10
11  ! print module variable pi:
12  print *, 'pi = ', pi
13
14  ! test the area function from module:
15  a = area(2.0_fp)
16  print *, 'area for a circle of radius 2: ', a
17
18end program circle_main2

Download this code

This example can be compiled and executed by typing

$ gfortran circle_mod2.f90 circle_main2.f90 -o circle.ex
$ ./circle.ex

In the next section we are going to learn more about Makefiles. This will greatly simplify our lives and let us stop thinking about the compilation process all the time.

The public and private keywords

By default all variables, subroutines, and functions defined in a module are accessible to any other module or program that can use it. We have already mentioned that using global variables can strongly limit the readability and maintainability of code, and by extension, make code very difficult to debug.

To combat this issue we can mark certain variables, functions, or subroutines as private. This means that they can only be referenced from within the module itself, and are inaccessible to any other users of the module. This means that we can design a few public subroutines and functions that interact with the internal structure of the module. This substantially improves readability because you know that all users are only modifying the internal state of your module through your public interface.

We can go one step further and make all contents private by default, then the only public members will be the ones we explicitly specify. Consider another iteration of the circle module:

 1! /codes/circle3/circle_mod3.f90
 2! Version where pi is a module variable, and contents default to private
 3
 4module circle_mod3
 5
 6  implicit none
 7  private
 8  
 9  integer, parameter, public :: fp = selected_real_kind(15)
10  real (fp), save :: pi
11
12  ! Public interface
13  public :: initialize
14  public :: area
15  public :: circumference
16
17contains
18
19  subroutine initialize()
20
21    ! Set the value of pi
22    pi = acos(-1.0_fp)
23
24  end subroutine initialize
25
26  real (fp) function area(r)
27    real (fp), intent(in) :: r
28    area = pi * r**2
29  end function area
30
31  real (fp) function circumference(r)
32    real (fp), intent(in) :: r
33    circumference = 2.0_fp * pi * r
34  end function circumference
35
36end module circle_mod3

Download this code

which could then be called from a main program like so:

 1! /codes/circle3/circle_main3.f90
 2
 3program circle_main3
 4
 5  use circle_mod3, only: fp, initialize, area
 6  implicit none
 7  real (fp) :: a
 8
 9  call initialize()   ! sets pi
10
11  ! print module variable pi:
12  ! print *, 'pi = ', pi
13
14  ! test the area function from module:
15  a = area(2.0_fp)
16  print *, 'area for a circle of radius 2: ', a
17
18end program circle_main3

Download this code

As before, this example can be compiled and executed by typing

$ gfortran circle_mod3.f90 circle_main3.f90 -o circle.ex
$ ./circle.ex

There are a few interesting things to note about this module and how we call it:

  • The private keyword on line 7 of the module sets all contents to default to private access

  • We need to explicitly mark subroutines and functions as public if we want users to have access to them

    • This example is a little artificial since we end up exporting all members

    • We’ll see more complicated code soon that makes direct use of this pattern

  • The pi module variable is private so we can not use it in the main program

    • Try importing it on the use line and uncomment the print statement to see how this fails