Fortran style guide

Basic formatting

Indentation, whitespaces, and line lengths are enforced by fprettify. Project settings are defined in the .fprettify.ini file located at the root of the repository.

Developers are encouraged to install the associated pre-commit Git hook. Additionally, it is recommended to configure your text editor to run fprettify automatically (e.g., on saving a file).

To preview changes without overwriting a file, use the –stdout option:

$ fprettify --config .fprettify.ini --stdout

Warning

CUDA Fortran chevron syntax is not supported by fprettify. To handle this, use !& to deactivate fprettify on lines where chevron syntax is used.

call gpu_kernel<<<blocks, threads>>>(args, ...) !&

Naming conventions

In the following, “symbol” is a catch-all term for variables, procedures, derived types, type components, and interfaces.

  • Use lowercase for all Fortran constructs (e.g., do, subroutine, module).

  • Symbols are named using the snake_case convention.

  • Variables declared with the parameter attribute are named using UPPER CASE letters. For all other names use lowercase.

  • Symbols should, as much as possible, be named after their meaning rather than their mathematical notation (e.g., velocity_field instead of u).

Procedure definitions

  • Procedure prototypes spanning more than 79 characters should be split before the first dummy argument is defined, after the opening parenthesis.

    subroutine my_long_subroutine( &
       argument1, argument2, argument3, argument4, argument5, argument6 &
       )
    
  • Function prototypes should indicate the return object type unless the return object is an array.

    pure function compute_area(radius) result(area)
      real :: compute_area
      real, intent(in) :: radius
      real :: area
    
      area = pi * radius**2
    end function compute_area
    
  • Functions should always be defined with the pure prefix. A procedure with side effects must be a subroutine.

Modules

  • Module names should start with the m_ suffix. Examples: module m_allocator, use m_allocator, only: ....

  • All module components should be private by default.

  • Always access module components using the only keyword:

    module m_a_module
       implicit none
       private
       public :: some_subroutine
    
       contains
    
       subroutine some_subroutine()
           ! Subroutine implementation
       end subroutine some_subroutine
    end module m_a_module
    
    ! Non-compliant
    use m_stencil
    
    ! Compliant
    use m_stencil, only: stencil_t
    

Derived type definitions

  • Derived type names should end with the _t suffix. Examples: allocator_t, type(stencil_t) :: s.

  • Omit the contains keyword if the type does not define any type-bound procedures.

  • All type components should be private by default.

    type :: allocator_t
       private
       ! Type components
    end type allocator_t
    
    type :: stencil_t
       private
       ! Type components
       contains
       procedure :: some_procedure
    end type stencil_t
    

Custom structure constructors

  • Name constructors as init or <type_root_name>_init.

  • Declare constructors with the private attribute.

  • Define constructors at the top of the module’s contains block.

Example:

module square_module
   implicit none
   private
   public :: square_t, create_square_from_square, create_square_default_color

   type :: square_t
      real :: size
      character(:), allocatable :: color
   end type square_t

   interface square_t
      module procedure create_square_from_square
      module procedure create_square_default_color
   end interface square_t

contains

   type(square_t) function create_square_from_square(sq_in)
      type(square_t), intent(in) :: sq_in
      ! Function implementation
      create_square_from_square%size = sq_in%size
      create_square_from_square%color = sq_in%color
   end function create_square_from_square

   type(square_t) function create_square_default_color(sq_size)
      real, intent(in) :: sq_size
      ! Function implementation
      create_square_default_color%size = sq_size
      create_square_default_color%color = 'blue'
   end function create_square_default_color

end module square_module

In-code documentation

x3d2 uses FORD to extract in-code documentation and generate HTML pages. The syntax for in-code documentation follows FORD’s syntax for comments. See the FORD User Guide for more details.

The body of modules, public types, public procedures, and public type-bound methods MUST be preceded by one or more documentation paragraphs. Optionally, the body of private symbols MAY be preceded by a documentation paragraph.

Procedure dummy arguments, interface components, and type-bound procedure declarations MAY be documented using an inline comment either on the same line or the next line directly following the statement (using two exclamation marks docmark !!). It is also possible to place documentation before the code which it is documenting (using the predocmark !>). See the example below for these two methods of documenting code.

Including LaTeX in in-code documentation

You can include LaTeX equations in your documentation. For inline math, use \( ... \). For displayed equations, you can use either $$ ... $$ or \[ ... \]. Note that $ ... $ is not supported for inline math. Displayed equations can be written using $$ ... $$, but this method does not number the equations. To create numbered equations, use the \begin{equation} ... \end{equation} environment. You can also use \label{eq:some_equation} to label the equations and \eqref{eq:some_equation} to reference them within the text.

Example:

subroutine add(a, b, c)
    !! This is the first paragraph of the procedure's
    !! documentation. Note that it starts with TWO !.
    !! The addition operation is defined in-line as \( c = a + b \).
    !!
    !! The following operation shows it as a displayed equation:
    !! $$c = a + b$$
    !!
    real, intent(in) :: a, b !! Optional documentation for dummy argument.
    real, intent(out) :: c !! The result of \( a + b \)

    ! The line below is a regular comment.
    ! Make use of the well-known addition algorithm.
    c = a + b

end subroutine add

Alternatively, it is possible to place documentation before the code which it is documenting using the predocmark !>. This is useful when the documentation is longer than a single line.

subroutine add(a, b, c)
    !> Optional documentation for dummy argument.
    real, intent(in) :: a, b
    !> Or we can document the code before the statement using the predocmark
    !> The result of \(a + b)\
    real, intent(out) :: c

    ! The line below is a regular comment.
    ! Make use of the well-known addition algorithm.
    c = a + b

end subroutine add