PFLOTRAN Fortran Formatting Protocol

Guidelines

  • Free format

  • Use & for continuation

  • Use ==, >, <, >=, <= instead of .eq., .eqv., .gt., .lt., .ge., .le..

  • Do not use goto statements (may not be possible in legacy code blocks)

  • Use two-space indentation with no tabs.

    subroutine PrintIJK()
    
      PetscInt :: i
    
      do k = 1, 100
        do j = 1, 100
          do i = 1, 100
            print *, i, j, k
          enddo
        enddo
      enddo
    
    end subroutine PrintIJK
    
  • Use a maximum source width of 80 characters. Use a continuation line beyond.

    !123456789+123456789+123456789+123456789+123456789+123456789+123456789+123456789+
                    print *, 'This sentence is just barely too long for a single &
                             &line.'
                    call LongSubroutineWithManyArguments(argument1, argument2, &
                                                         argument3, argument4)
    
  • Use a maximum of 32 characters for module/subroutine/variable names. Try to be as concise as possible.

  • Capitalization

  • All Fortran syntax should be lower case.

    module File_IO_module
    
      use Print_module
    
    contains
    
    subroutine PrintInfo()
    
      if () then
      else
      elseif
      endif
    
    end subroutine PrintInfo
    
  • Parameters are all caps.

    PetscInt, parameter, public :: MAXWORDLENGTH = 32
    character(len=MAXWORDLENGTH) :: word
    
  • All subroutines/function names should be CamelCase without underscores.

    call FileIOInit()
    call FileIODestroy()
    
  • All variable/class/derived type names should be lower case with underscores between words.

    PetscMPIInt :: my_rank
    type(abc_type) :: new_abc
    class(xyz_class_type), pointer :: new_xyz
    
  • All Fortran syntax with multiple words should have a space between words, except for conditionals and loops: elseif, endif, enddo.

    select case
    end select
    end subroutine
    

    Exceptions:

    elseif
    endif
    enddo
    
  • Pin all module, subroutine, function, and contains declarations up against the left side. This leaves more room for indentation.

  • The default private/public attribute for modules is private.

  • Place implicit none at the top of every module.

  • Use PetscReal instead of double precision or real*8.

  • Use PetscInt instead of integer.

  • Use PetscBool instead of logical.

  • Use PETSC_TRUE/PETSC_FALSE instead of .true./.false..

  • For array declarations, use the most concise and flexible format without the dimension statement.

    PetscReal :: array(6)
    PetscInt :: array_1D(6), array_2D(3,100)
    

    instead of

    PetscReal, dimension(6) :: array
    PetscInt, dimension(6) :: array_1D
    PetscInt, dimension(3,100) :: array_2D
    

    Note that arrays of the same data type may be declared on separate lines for clarity.

  • All variables in the function/subroutine argument list should be at the top of the routine with a blank line separating them from the ‘implicit none’. The local variables should come below with a blank line separating them from the variables in the subroutine argument list.

    subroutine Example(integer_in, real_in)
    
      implicit none
                               ! subroutine arguments declared first
      PetscInt :: integer_in
      PetscReal :: real_in
                               ! blank line separating local variables
      PetscBool :: whatever
      PetscInt :: i
      PetscInt :: integer1, integer2
      PetscInt :: iarray(5)
      PetscReal, pointer :: array(:)
    
  • All pointers used in with PETSc Vec data structures have an _p appended.

    PetscReal, pointer :: array_p
    
  • User appropriate spacing to improve readability:

    if(one_number>another_number.and.a_logical==PETSC_TRUE)then
    

    or

    if ( one_number>another_number.and.a_logical==PETSC_TRUE ) then
    

    are better viewed as

    if (one_number > another_number .and. a_logical == PETSC_TRUE) then
    
    pressure=rho*gravity*distance
    

    is better viewed as

    pressure = rho*gravity*distance
    
  • Use integer exponents (e.g., x**3) instead of real exponents (e.g., x**3.d0) whenever possible. With the integer approach, the compiler creates a series of multiplication (i.e., x*x*x) which is less expensive to calculate than the \(x^3 = e^{3 \ln x}\).

Filename and Module/Class Naming Convention

  • Modules and classes are Camel_Case with underscores between words and _module (or _class for Fortran 20XX classes) appended.

    Reaction_Sandbox_module
    Reaction_Sandbox_Example_class
    Reaction_Sandbox_Base_class
    
  • The corresponding filename is the module/class name with (1) _module or _class removed, (2) all lower case, and (3) ‘.F90’ appended.

    reaction_sandbox.F90
    reaction_sandbox_example.F90
    reaction_sandbox_base.F90
    
  • Files containing base classes are always named XXX_base.F90.

    simulation_base.F90
    reaction_sandbox_base.F90
    
  • Files containing functions/subroutines/modules that are often commonly shared between simulation modes, process models, or implementations are named XXX_common.F90.

    output_common.F90
    richards_common.F90
    
  • Files containing low level functions/subroutines or non-extended derived types are named XXX_aux.F90.

    output_aux.F90
    richards_aux.F90
    
  • Files containing functions/subroutines that serve as drivers for all classes of a derived type, should be named XXX.F90 where XXX is the root function.

    dataset.F90
    reaction_sandbox.F90
    

Example Fortran Source Code

An example source would be

module Example_module

  implicit none

  private  ! all variables/subroutines, etc. are private by default

#include "whatever.h"

  public :: ExampleCreate, ExampleGetTime

  PetscReal, save :: file_global_variable

contains

!************************************************************************** !

subroutine ExampleSetup(integer_in, real_in)
!
! Initializes the grid.
! Author: Jane Doe
! Date: 01/01/23
!
  use whatever_module

#include "whatever.h"

  PetscInt :: integer_in   ! note that the subroutine arguments are
  PetscReal :: real_in     ! declared first

  PetscBool :: whatever    ! note that declarations are group by type
  PetscInt :: i
  PetscInt :: integer1, integer2
  PetscReal :: real1, real2
  PetscReal :: real3, real4
  character(len=MAXWORDLENGTH) :: word
  PetscReal, pointer :: real_p(:)

  ...
  ! use the newer relational operators in logical expressions
  if (grid%ndof >= 2 .and. (.not.logical_whatever .or. &
                            integer1 /= integer2)) then
    do i=1,2
      call Whatever()
    enddo
  elseif (grid%ndof == 1 .and. &
          (.not.logical_whatever .or. integer1 == integer2)) then
    call SomethingElse()
  endif

  ! fortran select case (similar to C switch)
  select case (word)
    case ('flow')
      call Whatever
    case ('transport')
      call Whatever2(argument1, argument2, argument3, argument4, &
                     argument5)
  end select
  ...
  nullify(real_p)

end subroutine ExampleSetup

!************************************************************************** !

PetscReal function ExampleGetTime(...)
!
! Returns the current time in the simulation.
! Author: John Doe
! Date: 01/01/23
!
  use another_module

  implicit none

#include "whatever.h"

  PetscInt :: integer1
  PetscReal :: real1
  character(len=MAXWORDLENGTH) :: word

  ...
  ...
  ExampleGetTime = x

end function ExampleGetTime

end module Example_module