top of page

🧭 Best Practices for Using Subroutines and Functions in Modern Fortran

When developing large, performance-oriented programs—especially in FEM and optimization—it’s critical to keep your Fortran code modular, readable, and safe.

Below are four golden rules for managing subroutines and functions effectively in modern Fortran (Fortran 90+).  🔹1. Use Modules for Organization and Reusability

A module is like a toolbox — it groups related variables, constants, and functions in one unit, and lets other parts of the program access them using the use statement.

This avoids the “copy-paste jungle” of old F77 and guarantees type safety.

✅ Example

module math_utils

  implicit none

  private                      ! hide everything by default

  public :: vector_norm, dot_product  ! selectively expose useful routines



contains

  function vector_norm(x) result(norm)

    real(8), intent(in) :: x(:)

    real(8) :: norm

    norm = sqrt(sum(x**2))

  end function vector_norm



  function dot_product(a,b) result(res)

    real(8), intent(in) :: a(:), b(:)

    real(8) :: res

    res = sum(a*b)

  end function dot_product



end module math_utils

Now you can use it anywhere:

program test_mod

  use math_utils

  implicit none

  real(8) :: v(3) = [3.0, 4.0, 12.0]

  print *, 'Norm =', vector_norm(v)

end program test_mod

🔹 2. Pass Arguments Properly (Allocatable vs. Static Arrays)

Always pass data through arguments, not global variables.Modern Fortran supports allocatable arrays, which make your subroutines reusable and memory-safe.

⚙️ Allocatable Array Example



subroutine compute_stiffness(K, n)
  implicit none

  integer, intent(in) :: n

  real(8), allocatable, intent(out) :: K(:,:)

  allocate(K(n,n))

  K = 0.0

  K = 2.0  ! Example fill

end subroutine compute_stiffness

Usage:

program test_alloc
implicit none

  real(8), allocatable :: K(:,:)

  call compute_stiffness(K, 4)

  print *, 'K(1,1) =', K(1,1)

end program

Tip: In Fortran, you can’t mix fixed and deferred dimensions,


e.g. real(8), allocatable :: K(4,:) ❌ — use K(:,:) instead.


subroutine add_vectors(a, b, c)

  real(8), intent(in) :: a(3), b(3)

  real(8), intent(out) :: c(3)

  c = a + b

end subroutine

Use static dimensions only when the array size is known and constant (e.g., 4 DOFs per beam element).


🔹 3. Use contains Correctly and Never Declare a Function Like a Variable

Every program unit (module, subroutine, or program) that defines internal helper routines must include contains.

This separates main logic from local helper functions.

❌ Wrong

subroutine main_calc()

  real(8) :: helper  ! ❌ This declares a variable, not a function!

  call helper()

end subroutine

✅ Correct


subroutine main_calc()

  implicit none

  real(8) :: result

  result = helper(3.0)

  print *, 'Result =', result



contains

  function helper(x) result(y)

    real(8), intent(in) :: x

    real(8) :: y

    y = x**2

  end function helper



end subroutine main_calc

Now helper() is recognized as an internal function, scoped only to this subroutine.


No need for external declarations or interfaces.


.

🔹 4. Control Scope with private and public

By default, everything in a module is visible externally — which can lead to name conflicts or unwanted exposure.Use private to hide everything, then selectively mark what’s allowed to be used with public.

✅ Example




module fem_helpers

  implicit none

  private

  public :: assemble_beam, beam_stiffness



contains

  subroutine assemble_beam(K,F,ke,fe,i1,i2)

    real(8), intent(inout) :: K(:,:), F(:)

    real(8), intent(in) :: ke(4,4), fe(4)

    integer, intent(in) :: i1, i2

    integer :: dof(4)

    dof = [i1, i1+1, i2, i2+1]

    K(dof,dof) = K(dof,dof) + ke

    F(dof) = F(dof) + fe

  end subroutine assemble_beam



  function beam_stiffness(E,sec,L) result(ke)

    real(8), intent(in) :: E, sec(5), L

    real(8) :: ke(4,4)

    ! ... fill beam stiffness matrix ...

  end function beam_stiffness



  subroutine internal_debug()   ! not public → invisible outside

    print *, "Debug message"

  end subroutine internal_debug



end module fem_helpers

In the main program:


program fem_test

  use fem_helpers

  implicit none

  ! Can call assemble_beam and beam_stiffness

  ! Cannot call internal_debug

end program

Best Practice

Why It Matters

Key Syntax

Use module

Reusability, type safety

module ... contains ... end module

Pass arguments explicitly

Avoid globals, improve flexibility

intent(in/out), allocatable arrays

Use contains for internal helpers

Keeps functions local & safe

contains ... function/subroutine

Control visibility with private/public

Prevent name pollution

private, public :: func1, sub2


bottom of page