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:
- Receive data from the calling program (input).
- Return computed results back to the calling program (output).
- 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:
- intent(in) – The argument is read-only inside the subroutine. It cannot be modified.
- intent(out) – The argument is write-only. The subroutine assigns a value to it, which is returned to the caller.
- 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_nameis the name used in both the definition and thecallstatement.arg1, arg2, ...are the arguments passed from the calling program.- Declaring
intentensures 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:
- The main program initializes
x = 5. call double_value(x)passesxas an argument.- The subroutine reads
val, doubles it, and modifies it. - After the subroutine finishes,
xin the main program has been updated to10.
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:
numberis input-only (intent(in)), so the subroutine cannot modify it.squareis 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
xandyare inputs,max_valueis an output.- After the subroutine call,
max_valin 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
arrayis 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
- Mathematical Computations: Factorial, power, sums, and complex formulas.
- Array Processing: Scaling, summing, sorting, or transforming datasets.
- Simulation and Modeling: Updating variables like velocity, temperature, or pressure.
- Modular Design: Separating computations from display logic or input/output operations.
- 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
- Always declare intent: Makes your code clear and prevents accidental modifications.
- Use descriptive names: Helps readability and understanding of argument purpose.
- Keep subroutines focused: Each should perform a specific task.
- Avoid excessive use of inout: Only modify variables when necessary.
- Document your subroutine: Include input, output, and any assumptions.
- Handle arrays carefully: Ensure correct dimensions and bounds.
Leave a Reply