Skip to content

Fortran Modules

Alexandros Avdis edited this page Jun 16, 2014 · 10 revisions

Coding rules

All procedures should be placed in modules to ensure that explicit interfaces are always available. The only exception is procedures which are to be called directly from C: these must be external procedures. In addition, modules make common blocks obsolete. All variables which must be used by more than one program unit or which must persist when control passes away from fluidity must be placed in modules.

Tutorial

Modules provide a mechanism for grouping data, procedures and data types in one package which may be accessed by other programming units. Modules take the following form:

    module module_name

    ---- Declarations go here ----

    contains

    ---- Procedures go here ----

    end module module_name

The declarations part of the module can contain anything which you could declare in the declarations part of a subroutine. Any variables and types which are declared here will be available in any of the procedures declared in the module as well as anywhere where the module is used. It is almost always advisable to give the save attribute to any variables used in the module as this prevents the variables becoming undefined if control passes to a program unit which does not use the module.

The procedures part of the module can contain any function or subroutine declaration. Module procedures are similar to external procedures in that they can contain internal procedures. The syntax of the contains keyword is the same as for external procedures and so it must be omitted if the module contains no procedures (this requirement is removed in Fortran 2003).

Accessing modules

Unlike external procedures, module procedures are not automatically available to be called from other procedures. Any module or procedure which needs to access the module must use it:

    module user_module
    use module_name
    ! Note that use statements must be the first things in the scoping unit.
    implicit none

    ---- Other code using the contents of module_name ----

    end module user_module

It is also possible to import only part of a module:

    use module_name only: some_name, some_other_name

this usage is particularly appropriate for the Global_parameters module.

Compilation details

For obsure reasons to do with the way that Makefiles work, it is advantageous to name the files containing modules with a leading capital letter.

Because of the explicit access to the names of a module which comes with using that module, it is essential that each module is built before any of the program units which use it. This can be achieved in fluidity in two ways:

  • A module can be placed in a directory which is compiled before the directory which contains the program units which use the module. For modules containing utility routines not tied to a particular part of fluidity, the f90modules directory is a suitable choice.
  • Where the module and the using program unit are in the same directory, a dependency rule can be added to the Makefile.in in that directory.

Such dependencies take the form:

using_file.o: Module_name.o

This indicates that using_file is not to be built until Module_name.o is available and, further, that using_file.o should be rebuilt every time Module_name.o is rebuilt.

Remember to rerun 'configure' after editing Makefile.in.

Benefits of modules

As well as being a mechanism for code structure, modules offer a number of concrete benefits:

  • Compile-time checking of procedure calls is enabled so that errors due to argument mismatches will be caught earlier.

  • Module variables enable access to data such as global model parameters thereby reducing length of argument lists. This is achieved without the problems associated with common blocks. Derived data types can be shared between program units.

  • Modules enable proper namespace control so that name clashes are avoided or more easily overcome.

"Global" variables in modules

Global variables are declared on the top of a module before the "contains". Our policy is that it not acceptable to use global variables within modules, except in some very particular circumstances:

  • there's some caching in the very low level code parts of femtools (code you usually won't have to worry about)

  • some global parameters that are only set once at the very beginning of the run in the assembly code, to avoid having to read the option tree for each element, options are typically read before the element loop and stored in variables. To avoid having to pass these through all the different subroutines inside the element loop, they may be stored in module variables, provided that: they are simple logicals and parameters; they are set in one central place before the element loop; within the element loop they are only read, and not changed; they are only used within the central assembly subroutine of that module, and other subroutines that are only called from that subroutine. So don't use them in other subroutines inside that module that will be separately called from outside the module. Such a routine should just read these options again.

It is difficult to keep track of where global variables are set, used and changed (often an implicit assumption is made, without realizing it, that the variable is related to an object of which there can currently only be one) and we've had quite a few cases where we initially thought "oh we'll only ever have one external mesh, one Coordinate field, one free surface, one such and such input file, etc. etc." and later on had to go through quite a lot of pain to fix this when this was no longer true.

Procedure for moving code into modules

To move code in an existing source file into a module:

  1. Place the following statements around all code in the source file, excluding any copyright header and leading #include directives:

    module [name of module]        
        implicit none
        private
    contains
        ! Existing source file code
    end module [name of module]
  2. Replace any "end" statements used to end functions or subroutines with "end function [name of function]" or "end subroutine [name of subroutine]"

  3. Move all use statements within functions and subroutines to the head of the module and delete all "implicit none" statements from the functions and subroutines:

    module [name of module]
    
       ! All use statements here
    
        implicit none
    
       ! Rest of module
  4. Move all interfaces and externals to the head of the module:

    module [name of module]
    
        ! All use statements here
    
        implicit none
    
        private
    
        ! All interfaces here
    
        ! All external declarations here
    
    contains
    
        ! Rest of module
  5. Attempt to rebuild:

    make
  6. This (should) fail with linker errors, as the routines from this file have now been moved inside a module. For each linker error, make the function or subroutine public in the module, and add a use statement in the file causing the error, at the head of the module in that file, or at the head of the calling function or subroutine if the caller is not in a module:

    module [name of module]
    
        ! All use statements here
    
    private
    
    public :: [functions, subroutines]
    
    implicit none
    
        ! All interfaces here
    
        ! All external declarations here
    
    contains
    
        ! Rest of module
  7. Attempt to rebuild. The build can still fail; The most common reason is that there is a circular dependency (two modules, possibly via a number of use statements, attempting to use each other). This requires files be broken up in order to resolve the build dependencies.

    make
  8. If the build succeeds, clean and rebuild. This is required in order to check that the build dependencies are satisfied in the makefiles.

    make clean && make

    If this fails will an error that looks like:

    Fatal Error: Can't open module file...
    

    then edit the appropriate Makefile.in file and add the appropriate dependencies, e.g. for a file File1.F90 depending on file File2.F90:

    File1.o: File2.o

    Reconfigure and rebuild again:

    ./configure && make

Potential Problems

  • GCC permits modules to have the same name as functions or subroutines, wheras other compilers (e.g. Intel) do not. Modules must therefore not share a name with any subroutines or modules. Add "_module" to the module name if necessary.

  • GCC permits variables to have the same name as functions or subroutines, whereas other compilers (e.g. Intel, PGI) do not. This is a problem when functions that have previously been implicitly interfaced are moved into a module.

  • Interfaces split over multiple files can result in build problems with the Intel compiler. These can be resolved by, in the file that fails to compile, changing the order of the use statements for the modules containing the interfaces.

Clone this wiki locally