The .f90 suffix indicates free source form, not that the code conforms to the Fortran 90 standard. Don't use suffixes such as .f95, .f03, .f08, or .f18, which not all compilers and build systems recognize as free source form Fortran files.
Code in lower case is more readable than code in all upper case. Comments should be capitalized as normal written language.
It is easier for the reader to understand a write
statement if the format string is on the same line, rather than in a separate numbered format statement. If the same format string is used in multiple write
statements, define it as a parameter
.
This enables type-checking of arguments at compile time. If this is not possible, provide an explicit interface for the procedure.
If this is done, it is unnecessary to write implicit none
in each procedure.
Any argument that is strictly an input should be declared intent(in)
.
A function with an intent(out)
argument should be rewritten as a subroutine.
This tells the reader that they do not have side effects.
Use optional
arguments and derived types (which can store multiple entities) to reduce the number of required arguments.
The order of subroutine arguments should be required intent(in)
arguments, followed by required intent(out)
or intent(in out)
arguments, followed by optional
arguments
This will make it easier to call such a subroutine without naming the arguments. Argument declarations should be in the same order as the argument list.
It should appear after the declarations of function arguments but before the declarations of local variables.
In a procedure, integer :: i = 0
has a different meaning in Fortran than the analogous statement in other languages, and one should write integer, save :: i = 0
instead to alert the reader.
For example, call reg(lhs=a,rhs=b,coeff=x,stderr=c,sse=d)
is clearer than call reg(a,b,x,c,d)
For example, call sub(x1,x2,x3,x4) ! out: x3,x4
clarifies what the outputs of sub
are.
It was common in Fortran 77 libraries to overwrite an input matrix with output to conserve memory, but this is rarely necessary now. Instead use separate intent(in)
and intent(out)
array arguments.
This is prone to memory leaks when the function is used in an expression.
Pointer arrays must be explicitly deallocated to avoid a memory leak.
At the beginning of a function or subroutine, have at least one comment line explaining what it does
Have a comment explaining the meaning of each procedure argument, either separately or on the same line as the argument declaration
For example, use m, only: func,sub
instead of use m
. This clarifies for the reader what is being imported from each module.
This reduces namespace clashes and tells the reader what entities of a module are exported to other program units.
Do not put a stop
statement before the end of the main program or a return
statement before the end of a procedure.
Such statements are unnecessary. Use stop
only to end a program early or return
only to exit a procedure early.
This is unnecessary since such arrays are deallocated when they go out of scope.
If a derived type has allocatable
components that have related shapes, write a subroutine to allocate them consistently
Otherwise the code will need to be updated in several places if a new allocatable component is added to the definition of the derived type. Also write subroutines to read and write the derived type.
It is more flexible to pass a unit number to the subroutine and to write to that unit. The unit number argument can be optional
, and the local unit number can be set to standard output if the argument is not present
.
Set x_
to optional
argument x
if it is present
and to a default value otherwise. Consider using the optval
function of stdlib to set such shadow variables.
This clarifies to the reader and the compiler that the value is fixed and prevents inadvertent changes.
For example, set a parameter
such as data_unit
and output_unit
and use it in read
and write
statements. If you use integer literals to specify the unit in read
or write
statements, it's easy to mix them up.
Otherwise the numbers may be "mushed" together if they are larger than expected. Instead of
write (*,"(100f12.6)") x(1:n)
write
write (*,"(100(1x,f12.6))") x(1:n)
A formatted read
depends on the data file matching the format exactly and is error-prone.
The reader of a Fortran program expects variables such as i
or i1
to be integers and x
or x1
to be real.
If a program or procedure uses a published algorithm or is derived from another code, cite the publication or code
Avoid goto
, except possibly for error handling at the end of a main program or procedure. Avoid numbered lines except to delineate a block of code for error handling.
real :: vec(n1),mat(n1,n2)
is more concise than
real, dimension(n1) :: vec
real, dimension(n1,n2) :: mat
However,
real, dimension(n1,n2) :: a,b,c
is a good use of dimension
An exception is when you want an inline comment for each variable after its declaration.
For example, do iday=1,ndays
or do iatom=1,natoms
Refer to real :: x(n)
with x
instead of x(:)
or x(1:n)
. Use array sections for subsets of an array.
Use write (*,*) x
instead of write (*,*) (x(i),i=1,n)
if they have the same effect.
If you are passing multiple arrays to a procedure that must have the same dimensions, consider putting them in a derived type
For example, if a subroutine accepts arguments days(:)
, months(:)
, years(:)
that have a common length, probably a derived type should be defined containing (day,month,year). Then an array of that type can be passed.
For real(kind=wp) :: x
and integer :: i
write x = real(i,kind=wp)
instead of x = i
to make it clear that the conversion from integer to real is intentional. Do not use x = dble(i)
which is less flexible.
This aids legibility.
It helps the reader see what program unit is being terminated.
For example write z = c*x + d*y
Write x**2
not x**2.0
. The compiler may produce more efficient code for the former, which makes clear that the variable is raised to an exact integer power.
Write (1.0/i)**n
instead of 1.0/i**n
since the denominator may overflow in the latter expression.
Avoid defining a constant such as pi in multiple places.
This has been standard since Fortran 2003 and is supported by currently maintained compilers. The syntax is shorter and resembles that of other languages.
Use formatted output instead when this is desired.
Use the * unlimited repeat count edit descriptor of Fortran 2008 instead of a large integer repeat count
The danger of write (*,"(a,1000(1x,i0))") "abc",ivec(:)
is that size(ivec) > 1000
will cause an error. Instead use the format string "(a,*(1x,i0))"
The Fortran standard does not require this. Set such variables to zero explicitly if the code depends on this. Use a compiler option that sets variables to NaN or Inf so that the use of unitialized variables is detected.
A common error is for the input file not to match the read
statements of a program.
If many output files are created in a directory, at least print the directory name.
If a program must not crash within a procedure, use allocatable
rather than automatic
arrays for workspace
With the allocate
statement you can check the iostat
and handle errors if the program runs out of memory.
For production programs, print the results of the compiler_version
and compiler_options
intrinsics and the executable name by calling get_command_argument
This will make it easier to reproduce results.
It is not safe, for example, to write if (present(a) .and. a > 0) ainv = 1/a
. A nested if
where the condition present(a)
is tested first should be used instead.
Compilers implement random_number
differently. This is beneficial if you want to verify that simulation results are robust to the RNG algorithm used.
Some but not all compilers produce the same stream of variates on each run when random_number
is called without a prior call to random_seed
.
Compile with a standard conformance option and fix code that is not conformant. Consider fixing code that uses obsolescent features.
Use for example gfortran -std=f2018
or ifort -stand:f18
Compile with bounds-checking and other debugging options, until speed is needed for a production version.
Gfortran and Intel Fortran are both free and mature and should both be used, at the minimum. If you have a Fortran 95 code, consider testing it with g95, a mature but unmaintained Fortran 95 compiler.
Two sources of non-portability are Linux having case-sensitive file and directory names and Linux using a different directory separator.