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.