Style Guide
Please note that this style guide is new and a lot of code was written without it. As such it is likely that some portions of the code do not conform to following rules. If found, post their location in the Doc Gaps Issue, but feel free to correct them yourself.
Rules on files
Each source file must contain only a single module or program. All procedures (subroutines & functions) must be contained within modules. No old FORTRAN style separate files for procedures are allowed.
All source files should be free from and have a
.f90
of.F90
extension. Note that the capital letter is a convention that tells the compiler to use a preprocessor on the given file.It is preferable to have a single public class per module. It is not a strict rule and sometimes it is necessary to group number of classes together to avoid circular module dependencies. However, such cases should be avoided and clearly marked in module description. Also name of the module should correspond to name of the class. E.g. module
myType_class
must contain typemyType
.All files containing programs, should be placed into “Apps” folder of the source directory. No programs outside “Apps” folder are allowed.
Module file name must have exactly the same name as the module it contains.
Content of a module file must be clearly identified by its suffix
_func
contains a library of functions. (genericProcedures.f90 breaks it for now)_inter
contains and abstract class (abstract interface)._class
contains a class._mod
indicates a class-like (object-like) module. It is effectively an implementation of a Singleton design pattern. The module contains some data and access function to manipulate it. Its state can change during execution.Module with no suffix contains only global parameters (constants)
Rules on code
Every source file needs to use
implicit none
. No implicit typing is allowed. If you don’t know what implicit typing is, don’t think about it. Just make sure that implicit none is present at the beginning of a module or program e.g.module properModule_func implicit none private ! The above switches off implicit typing in the whole module ! including module procedures. public :: printHello contains !! !! Prints greating from Fortran. !! subroutine printHello() ! No "implicit none" is needed here. print *, "Hello, I am Fortran. I am a good programing language!" end subroutine printHello end module properModule_func
Keep variable, classes and procedure names descriptive. Try to keep them short. In general use lowerCamelCase, but break this rule for short variables like
N
for an integer orV
for velocity or any other physical variable with obvious meaning.Always include
numPrecision
module in a source file and use parametrisation of the variables e.g.program myProg use numPrecision implicit none integer(shortInt) :: i ! standard integer integer(longInt) :: score ! integer where large values are expected real(defReal) :: float ! Every real number logical(defBool) :: isHappy ! Every logical variable character(nameLen) :: neutronName ! Use for names of of objects, short strings character(pathLen) :: filePath ! Use to store paths to file end program myProg
Always explicitly import variables, types and functions from modules, unless module has now suffix and contains only global parameters.
program prog use numPrecision use endfConstants use dictionary_class, only : dictionary use nuclearData_inter, only : nuclearData ... end program prog
If you need to import a lot of modules group the use statements in some logically ordered blocks with some associated comment. Try to make reading of imported modules easy
program prog use numPrecision use endfConstants use dictionary_class, only : dictionary use nuclearData_inter, only : nuclearData ! Import factories use myInterface_func, only : new_myInterface use yourInterface_func, only : new_yourInterface ... end program prog
Every procedure definition needs to specify intent for its dummy variables. Type of the function is to be defined in its variable declarations. Dummy arguments must be defined in order of their appearance in argument list. Result type should be declared immediately after dummy arguments. Local variables are to be defined next. If procedure can return errors it should have a character parameter
Here
, which contains name of the function and file in which it is defined.pure recursive function factorial(n) result(fact) integer(shortInt), intent(in) :: n ! Value n connot be modified (attempt will produce compiler error) integer(shortInt) :: fact ! Define type of function result integer(shortInt) :: temp ! Define local variable character(100), parameter :: Here ='factorial (math_func.F90)' ! Location information for error reporting ... end function factorial
When using numeric constants include correct parametrisation.
real(defReal) :: x x = 9.76_defReal - OK x = 9.76_8 - WRONG x = 9.76 - WRONG
Note that
numPrecision
contains useful numeric parameters.real(defReal) :: x x = ONE x = TWO x = PI * TWO_PI
Use parameters in favour of numeric constants. They improve readability. Parameters should be in CAPITAL_LETTERS
integer(shortInt), parameter :: X_AXIS = 1 if(axis == X_AXIS) then - OK if(axis == 1) then - WRONG
Whitespaces and Indentation
Use no tabs. Only spaces. 2 Spaces per indentation level. It may be useful to configure your editor to insert 2 Spaces on Tab press.
We follow the rule that source code should not extend beyond the column 100.
When accessing components of derived types put space between % e.g.
a = myType % type2 % func() - OK a = myType%type2%func() - WRONG
In procedure calls add a single space between arguments e.g.
call mySubroutine(x, y(4), z) - OK call mySubroutine( x, y( 2 ), z) - WRONG call mySubroutine(x,y(2),z) - WRONG
It is useful to put whitespace around logical operators:
if (x /= 2) then - OK if (x/=2) then - WRONG
Comments and documentation
Every type or procedure needs to contain comment above itself marked with
!!
and with the description of the procedure or the type.Follow the following pattern for the procedure description
Note that when giving errors information, errors from procedures called by the procedure we document should not in general be included in the Errors: section. This is because these sub-procedures may change and any changes to their error behaviour would (most likely) not be propagated to the documentation of all procedures that use them, rendering their description invalid. However it would be best to use common sense and indicate when a procedure is depending on errors given by the other procedure. Just try to make Errors: section informative. Use your best judgement. Usually this section is the most useful part of documentation so help you fellow users and your future self.
Try to follow the following pattern for the derived type(class) description. Use the same format for the class-like modules (with _mod suffix)
If type you are writing can be build using a dictionary (usually from user input), include additional section with a sample input dictionary. Refer to dictionary input syntax Dictionary Input
Try to have a look at the code you wrote and just try to make it look pretty. Try to go back to your code after a break and try to spot places that seem unclear or confusing and improve them.
Comment your Code! Provide explanations for what given sections are doing, explain parts of the algorithms that may be confusing. To see what I mean you can have a look at the following code sample. I would argue it is easier to understand with comments then without them: