4.2. Appendix B: Coding standard

The numerous elements that make up the PENCIL CODE are written in a consistent style that has evolved since it was first created. Many people have contributed their knowledge and experience with in this and the result is what we believe is and extremely readable and manageable code.

As well as improving the readability of the code, by having some naming conventions for example aids greatly in understanding what the code does.

There is a standard for all aspects of the code, be it Fortran source, shell scripts, Perl scripts, LaTeX source, Makefiles, or otherwise. Where nothing has been explicitly stated it is recommended that similar existing examples found in the code are used as a template.

4.2.1. Fortran Code

The code should remain fully compatible with the Fortran90 standard. This ensures that the code will run on all platforms. Indeed, an important aspect of PENCIL CODE philosophy is to be maximally flexible. This also means that useful non-standard extensions to the code should be hidden in and be made accessible through suitable non-default modules.

Fortran is not case-sensitive but in almost all instances we prescribe some form of capitalization for readability.

In general all Fortran code including keywords, variable names etc. are written in lowercase. Some of the coding standard has already been discussed in Sect.~:ref:ProgrammingStyle. Here we discuss and amplify some remaining matters.

4.2.1.1. Indenting and whitespace

Whitespace should be removed from the end of lines.

Blank lines are kept to a minimum, and when occurring in subroutines or functions are replaced by a single !' in the first column.

Tab characters are not used anywhere in the code. Tab characters are not in fact allowed by the Fortran standard and compilers that accept them do so as an extension.

All lines are kept to be not more than 80 characters long. Where lines are longer they must be explicitly wrapped using the Fortran continuation character &. Longer lines (up to 132 characters) and additional spaces are allowed in cases where the readability of the code is enhanced, e.g., when one line is followed by a similar one with minor differences in some places.

Code in syntactic blocks such as if -- endif, do -- enddo, subroutine -- endsubroutine etc. is always indented by precisely two spaces. The exception to this is that nested loops where only the innermost loop contains executable code should be written with the do-- enddo pairs at the same level of indentation, using the following pattern:

do n=n1,n2
do m=m1,m2
  [...]
enddo
enddo

Alternatively nested loops may be written on a single line:

do n=n1,n2; do m=m1,m2
  [...]
enddo; enddo

4.2.1.2. Comments

Descriptive comments are written on their own lines unless there is a strong reason to do otherwise. Comments are never indented and the !' should appear in the first column followed by two spaces and then the text of the comment. Extremely short comments may follow at the end of a line of code, provided there is space.

Comments also must not exceed the 78 character line length and should be wrapped onto more lines as needed.

Typically comments should appear with a blank commented line above and below the wrapped text of the comment.

All subroutine/functions begin with a standard comment block describing what they do, when and by whom they were created and when and by whom any non-trivial modifications were made.

Comments should be written in sentences using the usual capitalization and punctuation of English, similar to how text is formatted in an e-mail or a journal article.

For example:

  some fortran code
  some more fortran code
!
!  A descriptive comment explaining what the following few lines
!  of code do.
!
  the fortran code being described
  the fortran code being described
  ...
!
!  A final detail described here.
!
  the final fortran code
  the final fortran code
  ...

Subroutines and functions are started with a comment block describing what they do, when and by whom they were created and when and by whom any non-trivial modifications were made. The layout of this comment block is a standard, for example:

!***********************************************************************
    subroutine initialize_density(f,lstarting)
!
!  Perform any post-parameter-read initialization i.e. calculate derived
!  parameters.
!
!  For compatibility with other applications, we keep the possibility
!  of giving diffrho units of dxmin*cs0, but cs0 is not well defined general.
!
!  24-nov-02/tony: coded
!   1-aug-03/axel: normally, diffrho should be given in absolute units
!

where dates are written in dd-mmm-yy format as shown and names appearing after the /' are either the users cvs login name or, where such exists amongst the PENCIL CODE community, the accepted short form (\(\approx 4\) characters) of the authors name.

4.2.1.3. Module names

The names of modules are written with initial letter capitalization of each word and the multiple words written consecutively without any separator.

4.2.1.4. Variable names

Variables are given short but meaningful names and written in all lowercase. Single character names are avoided except for commonly used loop indices and the two code data structures of the PENCIL CODE: f the main state array (see The f-array) and p the pencil case structure (see The pencil case).

Quantities commonly represented by a particular single character in mathematics are typically given names formed by repeating the character (usually in lowercase), e.g., the velocity \(u\) becomes uu, specific entropy \(s\) becomes ss etc.

Temperature in variable names is denoted with a capital T so as not to be confused with time as represented by a lowercase t. Note however that since Fortran is not case sensitive the variables, for example TT and tt, are the same so distinct names must be used. For this reason time is usually represented by a single t contrary to the above guideline.

The natural log of a quantity is represented by adding ln to its name, for example log of temperature would be lnTT.

There are some standard prefixes used to help identify the type and nature of variables as follows:

  • i – Denotes integer variables typically used as array indices.

  • i_ – Denotes pencil case array indices.

  • idiag_ – Denotes diagnostic indices.

  • l – Denotes logical/boolean flags

  • cdt – Denotes timestep constraint parameters.

  • unit_ – Denotes conversion code/physics unit conversion parameters.

4.2.1.5. Emacs settings

Here are some settings from wd’s ~/.emacs file:

;;; ~/.f90.emacs
;;; Set up indentation and similar things for coding the |PC|.
;;; Most of this can probably be set through Emacs' Customize interface
;;; as well.
;;; To automatically load this file, put the lines
;;;   (if (file-readable-p "~/.f90.emacs")
;;;       (load-file "~/.f90.emacs"))
;;; into your ~/.emacs file.

;; F90-mode indentation widths
(setq f90-beginning-ampersand nil) ; no 2nd ampersand at continuation line
(setq f90-do-indent           2)
(setq f90-if-indent           2)
(setq f90-type-indent         2)
(setq f90-continuation-indent 4)

;; Don't use any tabs for indentation (with TAB key).
;; This is actually already set for F90-mode.
(setq-default indent-tabs-mode nil)

;; Ensure Emacs uses F90-mode (and not Fortran-mode) for F90 files:
(setq auto-mode-alist
      (append
       '(
         ("\\.[fF]90$"   . f90-mode)
         ("\\.inc$"      . f90-mode)
        )
       auto-mode-alist))

;; Make M-Backspace behave in Xemacs as it does in GNU Emacs. The default
;; behavior is apparently a long-known bug the fix for which wasn't
;; propagated from fortran.el to f90.el.
;; (http://list-archive.xemacs.org/xemacs-patches/200109/msg00026.html):
(add-hook 'f90-mode-hook
          (function (lambda ()
             (define-key f90-mode-map [(meta backspace)] 'backward-kill-word)
)))

4.2.2. Other best practices

When implementing IF or SELECT blocks always write code for all cases – including the default or else case. This should be done even when that code is only a call to raise an error that the case should not have been reached. If you see a missing case anywhere then do add it. These failsafes are essential in a large multi-purpose multi-user code like the PENCIL CODE.

If a case is supposed to do nothing and it may be unclear that the coder has recognized this fact then make it explicit by adding the default case with a comment like

! Do Nothing

The compiler will clean away any such empty blocks.

4.2.3. General changes to the code

It is sometimes necessary to do major changes to the code. Since this may affect many people and may even be controversial among the developers, such changes are restricted to the time of the next PENCIL CODE User Meeting. Such meetings are advertised on http://www.nordita.org/software/pencil-code/ under the news section. Notes about previous such meetings can be found under http://www.nordita.org/software/pencil-code/UserMeetings/.

Major changes can affect those developers who have not checked in their latest changes for some time. Before doing such changes it is therefore useful to contact the people who have contributed to the latest developments on that module. If it is not functional or otherwise in bad shape, it should be moved to experimental, i.e. one says:

svn mv file.f90 experimental/file.f90

or:

git mv file.f90 experimental/file.f90

However, any such directory change constitutes a major change in itself and should be performed in agreement with those involved in the development. Otherwise any file that has been changed in the meantime will end up being outside revision control, which is to be avoided at all cost.