Subroutines with Arguments in Fortran

In Fortran, subroutines are modular program units designed to perform specific tasks. Unlike functions, which return a single value, subroutines can execute complex operations, modify variables, and return multiple results through arguments. This makes them extremely useful for modular programming, improving code readability, reusability, and maintainability in scientific and engineering applications.

Subroutines become even more powerful when combined with arguments, which allow data to be passed between the main program and the subroutine. This post provides an in-depth exploration of subroutines with arguments, including types of arguments, syntax, examples, practical applications, and best practices.

What Are Subroutine Arguments?

Arguments are variables passed to a subroutine that act as input, output, or both. They allow subroutines to:

  1. Receive data from the calling program (input).
  2. Return computed results back to the calling program (output).
  3. Modify data directly in the calling program (input-output).

Subroutines with arguments provide a flexible way to perform operations on variables without returning a single value as a function does.


Types of Subroutine Arguments

Fortran classifies subroutine arguments using the intent attribute, which clarifies how the argument is used:

  1. intent(in) – The argument is read-only inside the subroutine. It cannot be modified.
  2. intent(out) – The argument is write-only. The subroutine assigns a value to it, which is returned to the caller.
  3. intent(inout) – The argument can be read and modified, making it suitable for updating values.

Syntax of Subroutines with Arguments

The general structure of a subroutine with arguments is:

subroutine subroutine_name(arg1, arg2, ...)
! Declaration of argument types and intents
type, intent(in/inout/out) :: arg1, arg2, ...

! Code block performing operations
end subroutine subroutine_name
  • subroutine_name is the name used in both the definition and the call statement.
  • arg1, arg2, ... are the arguments passed from the calling program.
  • Declaring intent ensures clarity and allows the compiler to check for errors.

Example: Doubling a Value

A simple and illustrative example of a subroutine with an input-output argument:

program main
integer :: x
x = 5
call double_value(x)
print *, "Doubled value:", x
end program main subroutine double_value(val)
integer, intent(inout) :: val
val = val * 2
end subroutine double_value

Explanation:

  1. The main program initializes x = 5.
  2. call double_value(x) passes x as an argument.
  3. The subroutine reads val, doubles it, and modifies it.
  4. After the subroutine finishes, x in the main program has been updated to 10.

Output:

Doubled value: 10
  • Using intent(inout) allows the subroutine to modify the original variable.

Input Arguments: intent(in)

Arguments with intent(in) are read-only inside the subroutine. They allow data to be used without modifying it.

Example: Computing the Square

program main
integer :: x, result
x = 7
call compute_square(x, result)
print *, "Square of", x, "is", result
end program main subroutine compute_square(number, square)
integer, intent(in) :: number
integer, intent(out) :: square
square = number * number
end subroutine compute_square

Explanation:

  • number is input-only (intent(in)), so the subroutine cannot modify it.
  • square is output-only (intent(out)) and returns the computed result to the main program.

Output:

Square of 7 is 49

Output Arguments: intent(out)

Output arguments are write-only variables that carry results back to the caller. They must be assigned a value inside the subroutine before returning.

Example: Calculating Maximum of Two Numbers

program main
integer :: a, b, max_val
a = 10
b = 20
call find_max(a, b, max_val)
print *, "Maximum value is:", max_val
end program main subroutine find_max(x, y, max_value)
integer, intent(in) :: x, y
integer, intent(out) :: max_value
if (x > y) then
    max_value = x
else
    max_value = y
end if
end subroutine find_max
  • x and y are inputs, max_value is an output.
  • After the subroutine call, max_val in the main program contains the result.

Output:

Maximum value is: 20

Input-Output Arguments: intent(inout)

intent(inout) arguments allow both reading and writing, making them ideal for updating variables.

Example: Incrementing a Counter

program main
integer :: count
count = 0
call increment(count)
call increment(count)
print *, "Counter value:", count
end program main subroutine increment(counter)
integer, intent(inout) :: counter
counter = counter + 1
end subroutine increment

Output:

Counter value: 2
  • Each call updates the same variable in the main program.
  • This pattern is widely used for iterative computations.

Passing Arrays as Arguments

Subroutines can also operate on arrays as arguments. Array arguments can be input, output, or input-output.

Example: Doubling Array Elements

program main
integer :: arr(5), i
arr = (/1, 2, 3, 4, 5/)
call double_array(arr)
print *, "Doubled array:"
do i = 1, 5
    print *, arr(i)
end do
end program main subroutine double_array(array)
integer, intent(inout) :: array(5)
integer :: i
do i = 1, 5
    array(i) = array(i) * 2
end do
end subroutine double_array

Output:

Doubled array:
2
4
6
8
10
  • array is modified in-place in the subroutine, illustrating the power of input-output arguments.

Optional and Keyword Arguments in Subroutines

Fortran allows optional arguments and keyword passing for subroutines, increasing flexibility.

Example: Subroutine with Optional Argument

program main
integer :: x
x = 5
call scale(x)          ! Uses default scale factor
print *, "Scaled value:", x
call scale(x, 3)       ! Uses provided scale factor
print *, "Scaled value:", x
end program main subroutine scale(val, factor)
integer, intent(inout) :: val
integer, intent(in), optional :: factor
if (present(factor)) then
    val = val * factor
else
    val = val * 2
end if
end subroutine scale

Output:

Scaled value: 10
Scaled value: 30
  • present(factor) checks if the optional argument is provided.
  • Optional arguments provide flexibility without overloading subroutine names.

Practical Applications of Subroutines with Arguments

  1. Mathematical Computations: Factorial, power, sums, and complex formulas.
  2. Array Processing: Scaling, summing, sorting, or transforming datasets.
  3. Simulation and Modeling: Updating variables like velocity, temperature, or pressure.
  4. Modular Design: Separating computations from display logic or input/output operations.
  5. Iterative Algorithms: Counters, accumulators, and convergence checks.

Example: Updating Positions in a Simulation

program main
real :: positions(3) = (/0.0, 1.0, 2.0/)
real :: velocities(3) = (/0.5, 0.5, 0.5/)
call update_positions(positions, velocities, 2.0)
print *, "Updated positions:", positions
end program main subroutine update_positions(pos, vel, dt)
real, intent(inout) :: pos(3)
real, intent(in) :: vel(3), dt
integer :: i
do i = 1, 3
    pos(i) = pos(i) + vel(i) * dt
end do
end subroutine update_positions

Output:

Updated positions: 1.0 2.0 3.0
  • Subroutine updates array elements based on input arguments, a common pattern in simulations.

Best Practices for Subroutines with Arguments

  1. Always declare intent: Makes your code clear and prevents accidental modifications.
  2. Use descriptive names: Helps readability and understanding of argument purpose.
  3. Keep subroutines focused: Each should perform a specific task.
  4. Avoid excessive use of inout: Only modify variables when necessary.
  5. Document your subroutine: Include input, output, and any assumptions.
  6. Handle arrays carefully: Ensure correct dimensions and bounds.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *