.. _ch02-fortran-ftns-subr: ============================================================= Fortran subroutines and functions ============================================================= As we start writing more complicated software to solve more complicated problems we will need to stay organized. The first tool to keep our larger projects tidy is the ability to split off commonly used code into **functions** and **subroutines**. This achieves two goals simultaneously: * This chunks potentially complicated code into smaller, more digestible pieces. Code written this way is much easier to read and understand (and debug!). * This also means that code that needs to be re-used does not need to be re-written for each use. You can easily just call the function or subroutine from wherever it is needed. - The upshot of this is that if you revise how a piece of code works you don't need to go looking for every instance of it. Instead updating just the function/subroutine definition will accomplish your goal. Functions, external and internal ---------------------------------- Let's dive straight into some examples, and from there look at the differences between functions and subroutines. Here's an example of how to define and use your own function: .. literalinclude:: ./codes/fcn1.f90 :language: fortran :linenos: :download:`Download this code <./codes/fcn1.f90>` It prints out .. code-block:: console y = 2.00000000000000 z = 4.00000000000000 **Comments:** * Functions return only one value, and by default the name of the return value is the same as the function name, *see line 17* + The name of the return value can be different from the function name, which we will see momentarily * ``f`` is declared ``external`` in the main program to let the compiler know it is a function defined elsewhere rather than a variable * Before the ``function`` keyword on line 14 we need to declare the return type - This will change when we specify the return variable explicitly * We need the ever-present ``implicit none`` statement to cover the *scope* of the function * After ``implicit none`` we need to declare the types for all of the input variables - You could move ``implicit none`` after the argument declarations, this is mostly a matter of style as long as you declare *all* of your arguments * The ``intent(in)`` statement tells the compiler that ``x`` is a variable passed into the function that will not be modified in the function * Here the program and function are in the same file. Later, we will see how to break things up so that functions and subroutines can be defined in another file Alternatively, we can place the function *inside* our main program like this: .. literalinclude:: ./codes/fcn1_internal.f90 :language: fortran :linenos: :download:`Download this code <./codes/fcn1_internal.f90>` **Comments:** * Our function is no longer declared external * Our function is now defined *after* it gets used * For simple functions like this one, this pattern is preferred over the previous version. Better yet, you should consider putting function definitions away in their own module (more on this below). * Why is it that we could use ``fp`` to specify our precision in this case, but needed ``kind=8`` in the previous one? As a final iteration let's specify the return variable explcitly. This can be done as follows: .. literalinclude:: ./codes/fcn1_internal_retval.f90 :language: fortran :linenos: :download:`Download this code <./codes/fcn1_internal_retval.f90>` **Comments:** * The return type has moved into the function body next to the input type declarations * We can now give the function a nice clear name, but use a simpler variable inside for readability * We have also documented this function by providing comments desribing its purpose and all of its arguments - This is a very good habit to get into - You can even write *structured comments* that can aid documentation and editor features Overall, this final variant is the one you should use for all function definitions. It is substantially easier to read and understand. Modifying arguments ------------------- The input argument(s) to a function might also be modified, though this is not very common and is arguably bad practice (you should use a subroutine). Regardless, here is an example: .. literalinclude:: ./codes/fcn2.f90 :language: fortran :linenos: :download:`Download this code <./codes/fcn2.f90>` This produces .. code-block:: console Before calling f: y = 2.00000000000000 After calling f: y = 5.00000000000000 z = 4.00000000000000 The use of *intent* --------------------- You are not required to specify the intent of each argument, but there are several good reasons for doing so: * It makes your code more readable to other people. Immediately seeing the intent of each argument makes it much easier to reason about someone else's code. * It helps catch bugs. If you specify ``intent(in)``, and then the variable is changed in the function or subroutine, the compiler will give an error. * It can help the compiler produce machine code (e.g., ``*.o`` files) that runs faster. For example, if the optimizer knows that some variables will never change during execution, this fact can be used. Subroutines ------------- Functions are expected to produce a single output variable and examples like the one just given where an argument is modified are considered bad programming style. Subroutines are more flexible since they can have any number of inputs and outputs, or might not have any output variables whatsoever. For example, a subroutine might take an array as an argument and store the array to some file on disk without returning anything to the calling program. Here is a version of the same program as ``fcn1`` above, which uses a subroutine instead: .. literalinclude:: ./codes/sub1.f90 :language: fortran :linenos: :download:`Download this code <./codes/sub1.f90>` Comments: * Now we tell the compiler that ``x`` is an input variable and ``y`` is an output variable for the subroutine. * The ``intent`` declarations are optional but strongly recommended (see the above discussion) * Notice that ``fsub`` is not declared ``external``, actually it isn't declared at all prior to its use * What happens if we use ``contains`` to move the declaration inside our main program? What compiler flags could we use to test this change? Here is a version that works on an array ``x`` instead of a single value: .. literalinclude:: ./codes/sub2.f90 :language: fortran :linenos: :download:`Download this code <./codes/sub2.f90>` This produces .. code-block:: console 4.00000000000000 9.00000000000000 16.0000000000000 Comments: * The length of the array is passed into the subroutine as an argument. Note that this length is used to specify the ``dimension`` of the remaining arguments. + In Fortran 77 this was the only way to pass arrays as arguments, so many codes continue to use this pattern to match any legacy calls. * In Fortran 90 (and beyond) we can write subroutines and functions with array type arguments without passing the array size. This is preferred in modern codes. (See the example below) + Doing this requires that the function or subroutine be defined in a ``module`` or placed inside the program using ``contains``. + This has to do with implicit and explicit interfaces. Subroutine in a module ---------------------- Here is a version that avoids passing the length of the array into the subroutine. In order for this to work some additional *interface* information must be specified. This can be done by including the subroutine in a *module*. .. literalinclude:: ./codes/sub3.f90 :language: fortran :linenos: :download:`Download this code <./codes/sub3.f90>` Comments: * See the section :ref:`ch02-fortran-modules` for more information about modules. Note, in particular, that the module must be defined first and then used in the program via the ``use`` statement. * Typically modules will be placed in their own source files, rather than in the same file as the main ``program``. * The ``size`` intrinsic function lets you determine the array sizes internal to a subroutine, and makes it easy to write dimension independent code (but not rank independent). * Note that the use of the ``dimension`` keyword has been suppressed here compared to the previous example. Both syntaxes are valid, though you should choose one and stick to it for consistency. * The declaration of ``x`` in the subroutine uses ``x(:)`` indicates that it is an array with a single index (having `rank 1`), without specifying how long the array is. If ``x`` was a rank 2 array, indexed by ``x(i,j)``, then the declaration statement would use ``(:,:)``. * The declaration of ``f`` in the subroutine uses ``(size(x))`` to indicate that it is an array with one index and the length of the array is the same as that of ``x``. In fact it would be sufficient to tell the compiler that it is an array of rank 1. .. code-block:: fortran real (kind=8), dimension(:), intent(out) :: f ! Or real (kind=8), intent(out) :: f(:) but indicating that it has the same size as ``x`` is useful for someone trying to understand the code, and may enable certain compiler optimizations.