Coding conventions
Conventions for writing SWAT+ Fortran source. The goal is portable, readable, and robust code that compiles cleanly on every supported compiler and operating system.
Each item is tagged with one of three levels:
- Required. Mandatory. Pull requests that violate these will not be merged.
- Recommended. Strongly preferred. Deviations need justification.
- Encouraged. Optional. Apply for consistency where reasonable.
The list below is ordered by decreasing impact on code quality. Most subjective style items (indent width, capitalization) sit in the lower tiers.
Interoperability and portability¶
Required¶
- Source must conform to the ISO Fortran 95 standard.
- No compiler-specific or platform-specific extensions.
- No use of compiler-dependent error specifier values (
IOSTAT,STAT). - The source must compile with
gfortranfrom GCC, even if the production build uses the Intel compiler. EXIT(N)is the F90/95 standard exit. Prefer it toSTOP.STOPdoes not always return an error code. If you need an error code passed back to a script, useEXIT, and keep its call sites in one central place.
Recommended¶
- For floating-point precision and integer size, use the F90/95
KINDfeature. Do not depend on a vendor-supplied default precision flag. - Do not use tab characters. Tabs are not part of the Fortran character set and break portability.
Encouraged¶
- For code that interacts with external frameworks, declare every variable with a
KINDtype to make integration easier.
Readability¶
Required¶
- Free-form source.
- Consistent indentation, at least two spaces per level.
- Organize source into modules.
- Do not use Fortran keywords (
DATA, etc.) as variable names. - Use meaningful names for variables and parameters. Recognized abbreviations are fine when full names would be unwieldy.
- Every externally-called function or subroutine carries a header describing what it does, its arguments, and the author. Small internal routines can use a few descriptive comments instead.
- No magic numbers. Physical constants (pi, gas constants) go in
PARAMETERstatements, not hard-coded into expressions. - Do not hard-code numeric literals in argument lists. The compiler's default precision for literals is not guaranteed.
- No
GOTO. If you must use one, document why.
Recommended¶
- Name your loops and other constructs (subroutines, functions, modules, interfaces) where the name aids readability, especially in nested loops.
- Comment input, output, and local variables. Grouping comments across similar variables is fine when their names speak for themselves.
- Comment to mark major functional sections.
- Do not use Fortran statement names or intrinsic function names as symbols.
- Prefer named parameters over numeric literals:
REAL, PARAMETER :: PI = 3.14159, ONE = 1.0.
Encouraged¶
- In new code, follow your own consistent style. When editing existing code, match the style around you.
- Indent comments the same way as code.
- Group related procedures and data into modules.
- Keep lines to 132 characters or fewer.
- Use
<, >, <=, >=, ==, /=instead of.lt., .gt., .le., .ge., .eq., .ne.. - Name modules the same as the file that contains them. Avoid putting multiple modules in one file.
- Use blanks around operators, around
=, and in declarations to separate syntax elements. - Always use the
::notation in declarations, even when there are no attributes. - Line up attributes, variable names, and trailing comments vertically in declarations.
- Remove unused variables.
- Remove debug code once the bug is fixed.
Robustness¶
Required¶
IMPLICIT NONEin every program, module, and procedure.- Use
PRIVATEin modules by default, then explicitly mark what should bePUBLIC. Exception: modules that exist only to expose public data (constants, for example) can stay fully public. - Initialize every variable. Do not rely on the machine's default.
- Do not initialize a variable of one type with a value of another type.
Recommended¶
- Do not use
==or/=on floating-point values. Compare against an explicit tolerance instead (an epsilon check). - In mixed-type expressions and assignments, write the type conversion explicitly. Do not let the compiler do it implicitly.
- No
INCLUDEfiles. UseUSEwith modules. - Derived types belong in their own module, together with the procedures that operate on them.
- Each procedure should do one thing.
- Module-level (global) public variables should hold only static or rarely-changing data.
Encouraged¶
- Always use parentheses to make expression evaluation order explicit.
- Prefer derived types (
%field access) over loose collections of arrays.
Arrays¶
Required¶
- Subscript expressions must be integer.
- Do not assume any particular array passing mechanism when passing arrays as arguments.
Recommended¶
- Use array operations and intrinsics where they apply.
- Pass arrays as assumed shape.
Encouraged¶
- Declare
DIMENSIONon every non-scalar.
Dynamic memory and pointers¶
Required¶
- Prefer
ALLOCATABLEarrays over pointers where possible. Reduces leak and fragmentation risk. - Pointers are allowed when a subroutine needs to return a declared array to its caller.
- Initialize every pointer to
NULL()in its declaration:INTEGER, POINTER :: x => NULL(). - Prefer automatic arrays for transient allocations.
ALLOCATABLEandPOINTERarrays need explicitDEALLOCATE.
Recommended¶
- Always deallocate. Especially inside subroutines and loops.
- Check the
STATargument onALLOCATEandDEALLOCATE. - Do not repeatedly
ALLOCATE,DEALLOCATE, thenALLOCATEa larger block. Heap fragmentation follows.
Encouraged¶
- Dynamic allocation is preferred over fixed-size declarations with worst-case dimensions.
- Use automatic arrays in subroutines for simplicity.
Looping¶
Required¶
- No
GOTOfor loop exit or continuation. UseEXITorCYCLE.
Recommended¶
- No numbered
DOloops (DO 10 ... 10 CONTINUE).
Functions and procedures¶
Required¶
- Avoid
SAVE. Hold state in module variables instead. - No
ENTRYstatements in functions. - Functions must not return pointers.
- Do not name a user function the same as an intrinsic (
SUM, etc.). - A procedure that returns a single value should be a function. The value can be a derived type.
- Communicate through arguments or through the procedure's own module variables.
Recommended¶
- Every dummy argument that is not a pointer carries an
INTENT. - Avoid type-specific intrinsics (
AMAX,DMAX). Use the generic (MAX). - Do not declare static-dimensioned array arguments.
- Validate argument values where it matters.
Encouraged¶
- On an error, print a message that includes the routine name. You may terminate inside the routine, or pass an error flag back through the argument list.
- Use overloading when the same operation works on multiple types.
- Use modules or
CONTAINSfor explicit interfaces. - Avoid external routines. They need separate interface blocks that drift out of sync.
I/O¶
Required¶
- External-file I/O statements carry
err=,end=, andiostat=as appropriate. - All global variables, if any, are initialized at startup.
Recommended¶
- Avoid
NAMELISTI/O. - Use
WRITErather thanPRINTfor non-terminal I/O. - Use character parameters or in-line format specifiers in
READandWRITE. Do not use labeledFORMATstatements.
Obsolescent features to avoid¶
Required¶
- No common blocks. Use modules.
- No assigned or computed
GO TO. UseCASE. - No arithmetic
IF. Use a blockIF. - Use
REAL, notDOUBLE PRECISION. - Avoid
DATA,ASSIGN, labeledDO,BACKSPACE, blankCOMMON,BLOCK DATA. - Do not branch to an
END IFfrom outside itsIFblock. - No non-integer
DOloop counters. - No Hollerith constants.
- No
PAUSE. - No multiple
RETURN, no alternateRETURN.
Recommended¶
- No
EQUIVALENCE. Use pointers or derived types.
Encouraged¶
- Do not implicitly reshape arrays when passing them. This was common in F77 and is forbidden by F90 but still possible through external routines without interface blocks.
Source files¶
Required¶
- Document every function interface: argument name, type, units, description, constraints, defaults.
- No
INCLUDE. UseUSE. - Keep lines (including comments) under 80 columns where the language allows it.
Recommended¶
- Keep individual procedures under 300-500 effective lines.
- Separate statement blocks with blank lines or a marker character in column 1.
- Indent consistently.
- Module and subprogram names are lower case. The file is named after the contained module or subprogram, with
.f90.
Encouraged¶
- Separate argument declarations from local declarations visually.
- Use descriptive unique names. Keep them under 12-15 characters.
- Indent continuation lines so a multi-line expression lines up readably.
- Comments start with a marker character (
!). A standalone comment line starts the marker in column 1.