Fortran Style Guide
NEMO should be developed consistent with the NEMO contributors guide.
CanAM and CanCPL adopt the Fortran style guidelines used for CLASSIC
model,
with the following exceptions:
camelCaseis not required;snake_caseis allowedIndentation blocks can be 2 or 4 spaces, but new code must follow the convention used within an individual source code file.
The source code is now required to use the free-form format introduced in Fortran 90; fixed form (Fortran Fortran 77 and earlier) code will not be accepted. See Style Enforcement for info on how the standard is enforced.
Notable differences
For most CanAM developers, the following is a brief (not exhaustive) guide to the more
pervasive style changes:
Use free-format Fortran and not fixed form.
Denoting comments
Comment lines can be initiated anywhere and are denoted by
!A
Cor*in column 1 is not a valid way of indicating a comment line
Lines can now be longer than 72 characters (the style guide specifies 120 characters).
&is the character which denotes that the current line continues to the nextPutting a continuation character in column 6 does not continue the previous line
call mysub( foo, ! WRONG 1 bar, & faz, + baz ) call mysub( foo, & ! Correct! bar, & faz, & baz )
No arithmetic
ifstatements of the formif (arith_expr) lable1, lable2, lable3- logicalifstatements should be used instead.! An arithmetic if statement - WRONG if (x) 100, 110, 120 100 print*, "X is negative" go to 200 110 print*, "X is zero" go to 200 120 print*, "X is positive" 200 continue ! A logical if statement - CORRECT if (x < 0) then print*, "X is negative" else if (x == 0) then print*, "X is zero" else print*, "X is positive" end if
Developers should no longer work in ALL CAPS, and instead use lowercase to improve readability for other developers.
All variables must be explicitly typed.
Programs, modules, and interfaces must contain
implicit noneSubroutines and functions outside of modules must have
implicit noneCustom implicit rules (e.g.
implicit real, (a-b)are not allowed)
Variables in a subroutine or function signature must be declared on their own line with explicit type,
intent, and documentation (where “signature” refers to variables/objects sent in through thesubroutine/functioncall.)!> Returns the pressure at a given layer subroutine mysub(foo, bar, baz) integer, intent(in ) :: foo !< The vertical model index \f$[unitless]\f$ real, intent( out) :: bar !< Pressure of the layer foo \f$[Pa]\f$ logical, intent(inout) :: baz !< If true, `bar` is defined at the interface, otherwise !! at the midpoint of the cell. end subroutine mysub
Developers unfamiliar with the
intentkeyword are encouraged to reference online resources for detailed examples or explanations (i.e. see here, here or here) but in summary,intentis used to define the intended use of an argument within afunctionorsubroutine, and helps the compiler pick up potential bugs. When defining theintent, it can take the following three values -in,out, orinout, where:intent(in)- the argument should only be used to pass information into the routine (i.e. the value should not be changed)intent(out)- the argument should only be used to pass information out of the routine (i.e. it is meant to just hold a result)intent(inout)- the argument can be used to carry information into and out of the routine (i.e. sending a variable that is used in a calculation and then updated)
Additionally, it should be noted that:
while
intenthelps compilers pick up potential bugs, when routines don’t have explicitinterfaceblocks, or exist within amodule, they are unable to pick upintentviolations in nested routine calls as they have no way of checking that the routine signatures match.when not used, the compilers generally assume that the variables should be treated as
intent(inout)- but it must be explicitly stated that they are not exactly equivalent (interested readers who wish to go down a technical rabbit hole can begin here) .
Array variables should be declared using the
dimensionattributereal :: array(ni,nj,nk) ! WRONG real, dimension(ni,nj,nk) :: array ! Correct
Variables must be declared using
::to separate the variable type and name, e.g.integer foo ! WRONG integer :: foo ! Correct
No new Holleriths can be introduced - developers should use
characterarrays to store strings instead, e.g.integer :: string_var = 3HPSL ! WRONG character(len=3) :: string_var = 'PSL' ! Correct
No new
go tostatements can be added.with the new standards, it has become highly recommended that
go tostatements are avoided, and thus new instances ofgo tos will not be accepted in any newCanAMcode.if working within a pre-existing looping structure and wish to use
go toto skip some code within the loop, see Fortran’scycleorexitcommands. For other situations, developers will need to reconsider the control flow of their program. Different branching/loop structures should be used to avoid the need for these statements. For additional guidance on these structures seeFortran - LoopsandFortran - Decisionshere
Style Enforcement
During the initial syntax upgrade, all code within CanAM was updated to free-format
Fortran and checked for compliance with the CLASSIC style guidelines with the
two exceptions noted above. Going forward, all new code will be assessed to confirm that
it meets the standards laid out above.
Note
Contributions with style violations will not be accepted into the main branch
To make sure the code meets the required standards, it will be checked both
automatically by a code analysis tool (referred to as a linter) that checks for obvious style violations, and
manually during merge request review
Linter Checks
A large portion of the style requirements will need to be enforced through the code review process, but during the automatic checking, the linter breaks things into “Warnings” and “Errors”, where the errors cause the checks to “Fail”, and the warnings do not, but the code reviewers may still ask the developer to fix the code to avoid the warnings.
Errors:
implicit variables - i.e. the code does not use
implicit noneintentmissing from required variablesvariables defined without the
::separatorfixed form specific syntax is used - i.e. using
Corcfor comment linesnew arithmetic
ifstatements addednew hollerith variables added
new
go tostatements addednote that
go tostatements that were not able to be trivially removed were left and will be kept until work is done to remove them. New go to statements are not accepted.
Warnings:
multiple variables defined on one line
this is acceptable for local variables, such as index vars like
i,j,k, which is why it is just a warning.
Using the linter manually
For developers who would like to check their code manually, the linter can also be invoked interactively, and
documentation has been provided in
to help in this process. As an example, to check the code within CanAM, provided the developer is working
within a suitable python environment, all that needs to be done is:
cd path/to/CanAM
python ../CCCma_tools/linter/fast.py # or python3, depending on your environment
Notes on Newer Fortran Standards
It should be noted that while Fortran 90 represents a major revision to Fortran standards there have been further revisions to the language since, with the most recent standard being published in 2018. Specifically, further revisions to the standard include:
Unlike the change from Fortran 77 to Fortran 90, the syntax of the language remains similar between all the standards. All standards subsequent to Fortran 90 are fully backwards compatible with the primary differences being the inclusion of new features which include support for more object-oriented programming, portability across machines, and interoperability with C. Developers are encouraged to learn more about these standards and incorporate them where appropriate in their own development.