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:
1. ``camelCase`` is not required; ``snake_case`` is allowed
2. Indentation 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 ``C`` or ``*`` 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 next
* Putting a continuation character in column 6 **does not** continue the previous line
.. code-block:: fortran
call mysub( foo, ! WRONG
1 bar,
& faz,
+ baz )
call mysub( foo, & ! Correct!
bar, &
faz, &
baz )
* **No arithmetic** ``if`` **statements** of the form ``if (arith_expr) lable1, lable2, lable3``
- logical ``if`` statements should be used instead.
.. code-block:: fortran
! 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 none``
* Subroutines and functions outside of modules must have ``implicit none``
* Custom 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 the ``subroutine``/``function`` call.)
.. code-block:: fortran
!> 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 ``intent`` keyword are encouraged to reference online resources for
detailed examples or explanations (i.e. see `here `_, `here `_
or `here `_)
but in summary, ``intent`` is used to define the *intended* use of an argument within a ``function`` or ``subroutine``,
and helps the compiler pick up potential bugs. When defining the ``intent``, it can take the following three values -
``in``, ``out``, or ``inout``, 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 ``intent`` *helps* compilers pick up potential bugs, when routines don't have explicit ``interface`` blocks, or
exist within a ``module``, they are unable to pick up ``intent`` violations 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 ``dimension`` attribute
.. code-block:: fortran
real :: 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.
.. code-block:: fortran
integer foo ! WRONG
integer :: foo ! Correct
#. No new Holleriths can be introduced - developers should use ``character`` arrays to store strings instead, e.g.
.. code-block:: fortran
integer :: string_var = 3HPSL ! WRONG
character(len=3) :: string_var = 'PSL' ! Correct
#. No new ``go to`` statements can be added.
* with the new standards, it has become highly recommended that ``go to`` statements are avoided, and
thus *new* instances of ``go to``\s will not be accepted in any new ``CanAM`` code.
* if working within a pre-existing looping structure and wish to use ``go to`` to skip some code
within the loop, see Fortran's ``cycle`` or ``exit`` commands. 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
see ``Fortran - Loops`` and ``Fortran - Decisions``
`here `_
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 none``
* ``intent`` missing from required variables
* variables defined without the ``::`` separator
* fixed form specific syntax is used - i.e. using ``C`` or ``c`` for comment lines
* new arithmetic ``if`` statements added
* new hollerith variables added
* new ``go to`` statements added
* note that ``go to`` statements 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:
.. code-block:: bash
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:
- `Fortran 95 `_
- `Fortran 2003 `_
- `Fortran 2008 `_
- `Fortran 2018 `_
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.