After completing this exercise, you will be able to:
intent attributes to specify argument behaviorcontains statement for internal subprogramsIf you're coming from C or C++, you're used to having one type of callable unit: the function. In Fortran, there are two distinct types:
| Feature | C/C++ Function | Fortran Function | Fortran Subroutine |
|---|---|---|---|
| Returns a value | Yes (or void) | Yes, exactly one | No |
| How called | result = func(args) |
result = func(args) |
call sub(args) |
| Modifies arguments | Possible (via pointers) | Possible (but discouraged) | Primary purpose |
| Use in expressions | Yes | Yes | No |
call statement.! Function usage - returns a value, used in expression
area = circle_area(radius)
total = sum_values(a, b) + sum_values(c, d)
! Subroutine usage - called with 'call', modifies arguments
call swap(x, y)
call read_data(filename, data_array, num_elements)
void function that modifies pointer arguments. In Fortran, this pattern is handled by subroutines. Functions in Fortran are meant to be "pure" computations that return a result.
There are two common styles for declaring functions in Fortran:
Style 1: Return type before function name
real function circle_area(radius)
implicit none
real, intent(in) :: radius
real, parameter :: PI = 3.14159265
circle_area = PI * radius * radius
end function circle_area
Style 2: Result variable (more flexible)
function circle_area(radius) result(area)
implicit none
real, intent(in) :: radius
real :: area
real, parameter :: PI = 3.14159265
area = PI * radius * radius
end function circle_area
Both styles are equivalent. Style 2 is useful when the function name is long or when you want a more descriptive result variable name.
program function_demo
implicit none
real :: r, a, c
print *, "Enter the radius of a circle: "
read *, r
! Call functions in expressions (just like C/C++)
a = circle_area(r)
c = circle_circumference(r)
print '(A, F10.4)', "Area: ", a
print '(A, F10.4)', "Circumference: ", c
! Functions can be used directly in output
print '(A, F10.4)', "Double area: ", 2.0 * circle_area(r)
contains
real function circle_area(radius)
implicit none
real, intent(in) :: radius
real, parameter :: PI = 3.14159265
circle_area = PI * radius * radius
end function circle_area
real function circle_circumference(radius)
implicit none
real, intent(in) :: radius
real, parameter :: PI = 3.14159265
circle_circumference = 2.0 * PI * radius
end function circle_circumference
end program function_demo
program multi_arg_function
implicit none
real :: x1, y1, x2, y2, dist
x1 = 0.0; y1 = 0.0
x2 = 3.0; y2 = 4.0
dist = distance(x1, y1, x2, y2)
print '(A, F10.4)', "Distance: ", dist ! Should print 5.0
contains
real function distance(x1, y1, x2, y2)
implicit none
real, intent(in) :: x1, y1, x2, y2
distance = sqrt((x2 - x1)**2 + (y2 - y1)**2)
end function distance
end program multi_arg_function
program various_functions
implicit none
integer :: n, fact
character(len=50) :: name, upper_name
n = 5
fact = factorial(n)
print '(A, I0, A, I0)', "Factorial of ", n, " is ", fact
name = "hello world"
upper_name = to_uppercase(name)
print '(A, A)', "Uppercase: ", trim(upper_name)
contains
integer function factorial(n)
implicit none
integer, intent(in) :: n
integer :: i
factorial = 1
do i = 2, n
factorial = factorial * i
end do
end function factorial
function to_uppercase(str) result(upper)
implicit none
character(len=*), intent(in) :: str
character(len=len(str)) :: upper
integer :: i, ic
upper = str
do i = 1, len_trim(upper)
ic = ichar(upper(i:i))
if (ic >= ichar('a') .and. ic <= ichar('z')) then
upper(i:i) = char(ic - 32)
end if
end do
end function to_uppercase
end program various_functions
Subroutines are the workhorses of Fortran. They can:
subroutine swap(a, b)
implicit none
integer, intent(inout) :: a, b
integer :: temp
temp = a
a = b
b = temp
end subroutine swap
Unlike functions, subroutines must be called with the call statement:
program subroutine_demo
implicit none
integer :: x, y
x = 10
y = 20
print *, "Before swap: x =", x, ", y =", y
call swap(x, y)
print *, "After swap: x =", x, ", y =", y
contains
subroutine swap(a, b)
implicit none
integer, intent(inout) :: a, b
integer :: temp
temp = a
a = b
b = temp
end subroutine swap
end program subroutine_demo
Output:
Before swap: x = 10 , y = 20
After swap: x = 20 , y = 10
This is where subroutines excel—returning multiple values:
program multi_output
implicit none
real :: a, b, c
real :: sum_val, product_val, average_val
a = 10.0
b = 20.0
c = 30.0
call compute_statistics(a, b, c, sum_val, product_val, average_val)
print '(A, F10.2)', "Sum: ", sum_val
print '(A, F10.2)', "Product: ", product_val
print '(A, F10.2)', "Average: ", average_val
contains
subroutine compute_statistics(x, y, z, total, prod, avg)
implicit none
real, intent(in) :: x, y, z
real, intent(out) :: total, prod, avg
total = x + y + z
prod = x * y * z
avg = total / 3.0
end subroutine compute_statistics
end program multi_output
| Scenario | Use Function | Use Subroutine |
|---|---|---|
| Compute single value | ✅ | |
| Need multiple outputs | ✅ | |
| Use result in expression | ✅ | |
| Modify input arguments | ✅ | |
| Perform I/O operations | ✅ (recommended) | |
| Initialize data structures | ✅ |
By default, Fortran passes all arguments by reference, meaning the subroutine or function receives the actual memory location of the variable, not a copy. This is similar to passing pointers in C or references in C++.
subroutine double_it(x)
implicit none
integer :: x ! No intent specified - can be modified!
x = x * 2 ! This CHANGES the original variable
end subroutine double_it
To make your code safer and clearer, Fortran provides the intent attribute:
| Intent | Meaning | Compiler Behavior |
|---|---|---|
intent(in) |
Input only—cannot be modified | Error if you try to modify |
intent(out) |
Output only—must be assigned | Undefined on entry |
intent(inout) |
Both input and output | Can read and modify |
program intent_demo
implicit none
integer :: a, b, result
a = 5
b = 3
call add_numbers(a, b, result)
print *, "Sum:", result
contains
subroutine add_numbers(x, y, sum)
implicit none
integer, intent(in) :: x, y ! Cannot modify x or y
integer, intent(out) :: sum ! Must assign sum
sum = x + y
! x = 0 ! ERROR: Cannot modify intent(in) argument
end subroutine add_numbers
end program intent_demo
intent attributes. They:
Arrays can be passed in several ways:
Explicit-shape arrays (size passed as argument):
subroutine process_array(arr, n)
implicit none
integer, intent(in) :: n
real, intent(inout) :: arr(n)
integer :: i
do i = 1, n
arr(i) = arr(i) * 2.0
end do
end subroutine process_array
Assumed-shape arrays (modern, preferred):
subroutine process_array(arr)
implicit none
real, intent(inout) :: arr(:) ! Shape determined at runtime
integer :: i
do i = 1, size(arr)
arr(i) = arr(i) * 2.0
end do
end subroutine process_array
Complete array passing example:
program array_passing
implicit none
real :: data(5) = [1.0, 2.0, 3.0, 4.0, 5.0]
real :: total, avg, min_val, max_val
print *, "Original array:", data
call array_stats(data, total, avg, min_val, max_val)
print '(A, F8.2)', "Sum: ", total
print '(A, F8.2)', "Average: ", avg
print '(A, F8.2)', "Minimum: ", min_val
print '(A, F8.2)', "Maximum: ", max_val
call scale_array(data, 2.0)
print *, "Scaled array:", data
contains
subroutine array_stats(arr, total, avg, min_val, max_val)
implicit none
real, intent(in) :: arr(:)
real, intent(out) :: total, avg, min_val, max_val
total = sum(arr)
avg = total / real(size(arr))
min_val = minval(arr)
max_val = maxval(arr)
end subroutine array_stats
subroutine scale_array(arr, factor)
implicit none
real, intent(inout) :: arr(:)
real, intent(in) :: factor
arr = arr * factor ! Whole-array operation
end subroutine scale_array
end program array_passing
program allocatable_passing
implicit none
real, allocatable :: data(:)
integer :: n
print *, "Enter number of elements: "
read *, n
allocate(data(n))
call fill_array(data)
call print_array(data)
deallocate(data)
contains
subroutine fill_array(arr)
implicit none
real, intent(out) :: arr(:)
integer :: i
do i = 1, size(arr)
arr(i) = real(i * i)
end do
end subroutine fill_array
subroutine print_array(arr)
implicit none
real, intent(in) :: arr(:)
integer :: i
print *, "Array contents:"
do i = 1, size(arr)
print '(A, I3, A, F8.2)', " arr(", i, ") = ", arr(i)
end do
end subroutine print_array
end program allocatable_passing
The contains statement allows you to define internal subprograms within a program or another subprogram. This is similar to nested functions in some languages.
program contains_demo
implicit none
real :: global_scale = 2.0 ! Accessible to internal subprograms
real :: values(5) = [1.0, 2.0, 3.0, 4.0, 5.0]
print *, "Original:", values
call apply_scale(values)
print *, "Scaled: ", values
contains
subroutine apply_scale(arr)
implicit none
real, intent(inout) :: arr(:)
! global_scale is accessible here via host association
arr = arr * global_scale
end subroutine apply_scale
end program contains_demo
program my_program
implicit none
! Variable declarations
! Executable statements
contains
function my_function(args)
! Function body
end function my_function
subroutine my_subroutine(args)
! Subroutine body
end subroutine my_subroutine
end program my_program
Modules are Fortran's primary mechanism for:
module math_utils
implicit none
! Module variables (accessible to all who use this module)
real, parameter :: PI = 3.14159265358979
real, parameter :: E = 2.71828182845905
contains
real function circle_area(radius)
real, intent(in) :: radius
circle_area = PI * radius * radius
end function circle_area
real function circle_circumference(radius)
real, intent(in) :: radius
circle_circumference = 2.0 * PI * radius
end function circle_circumference
subroutine degrees_to_radians(degrees, radians)
real, intent(in) :: degrees
real, intent(out) :: radians
radians = degrees * PI / 180.0
end subroutine degrees_to_radians
end module math_utils
program use_module_demo
use math_utils ! Import the module
implicit none
real :: r, angle_deg, angle_rad
r = 5.0
print '(A, F10.6)', "PI = ", PI ! Access module constant
print '(A, F10.4)', "Area of circle with r=5: ", circle_area(r)
angle_deg = 90.0
call degrees_to_radians(angle_deg, angle_rad)
print '(A, F6.1, A, F10.6)', "90 degrees = ", angle_rad, " radians"
end program use_module_demo
onlyYou can import only specific items from a module:
program selective_import
use math_utils, only: PI, circle_area ! Import only what we need
implicit none
print *, "PI =", PI
print *, "Area =", circle_area(3.0)
! print *, "E =", E ! ERROR: E was not imported
end program selective_import
program rename_import
use math_utils, only: PI, area => circle_area ! Rename circle_area to area
implicit none
print *, "Area =", area(3.0) ! Use the renamed function
end program rename_import
Control what users of your module can access:
module controlled_access
implicit none
private ! Default: everything is private
public :: public_function ! Explicitly make public
integer :: internal_counter = 0 ! Private - cannot be accessed outside
contains
function public_function(x) result(y)
real, intent(in) :: x
real :: y
call internal_helper()
y = x * 2.0
end function public_function
subroutine internal_helper()
! Private subroutine - only callable within this module
internal_counter = internal_counter + 1
end subroutine internal_helper
end module controlled_access
module statistics
implicit none
private
public :: mean, variance, std_dev, describe_array
contains
real function mean(arr)
real, intent(in) :: arr(:)
mean = sum(arr) / real(size(arr))
end function mean
real function variance(arr)
real, intent(in) :: arr(:)
real :: avg, sum_sq
integer :: i
avg = mean(arr)
sum_sq = 0.0
do i = 1, size(arr)
sum_sq = sum_sq + (arr(i) - avg)**2
end do
variance = sum_sq / real(size(arr))
end function variance
real function std_dev(arr)
real, intent(in) :: arr(:)
std_dev = sqrt(variance(arr))
end function std_dev
subroutine describe_array(arr)
real, intent(in) :: arr(:)
print *, "=== Array Statistics ==="
print '(A, I0)', " Count: ", size(arr)
print '(A, F12.4)', " Sum: ", sum(arr)
print '(A, F12.4)', " Mean: ", mean(arr)
print '(A, F12.4)', " Variance: ", variance(arr)
print '(A, F12.4)', " Std Dev: ", std_dev(arr)
print '(A, F12.4)', " Minimum: ", minval(arr)
print '(A, F12.4)', " Maximum: ", maxval(arr)
end subroutine describe_array
end module statistics
program stats_demo
use statistics
implicit none
real :: data(10) = [23.5, 45.2, 12.8, 67.3, 34.1, &
89.4, 21.7, 56.9, 43.2, 78.6]
call describe_array(data)
end program stats_demo
When a subprogram is defined outside of your program (not in contains or a module), Fortran doesn't automatically know its interface (argument types, return type, etc.). This can lead to subtle bugs.
| Situation | Interface Needed? |
|---|---|
Internal subprogram (in contains) |
No - automatic |
Subprogram in a module (use module) |
No - automatic |
| External subprogram in separate file | Yes |
| Assumed-shape array arguments | Yes |
| Pointer or allocatable arguments | Yes |
| Optional arguments | Yes |
| Keyword arguments | Yes |
interface
function function_name(args) result(res)
type, intent(in) :: args
type :: res
end function function_name
subroutine subroutine_name(args)
type, intent(...) :: args
end subroutine subroutine_name
end interface
File: external_func.f90
real function square(x)
implicit none
real, intent(in) :: x
square = x * x
end function square
File: main_no_interface.f90 (Incorrect — works by luck)
program no_interface_demo
implicit none
real :: result, square ! Declaring square as real is NOT an interface!
result = square(5.0) ! May work, but no type checking!
print *, "Result:", result
end program no_interface_demo
File: main_with_interface.f90 (Correct)
program with_interface_demo
implicit none
real :: result
! Explicit interface tells compiler about the function
interface
real function square(x)
real, intent(in) :: x
end function square
end interface
result = square(5.0) ! Compiler can now check types
print *, "Result:", result
end program with_interface_demo
If a subprogram takes an assumed-shape array (arr(:)), an explicit interface is required:
File: array_ops.f90
subroutine double_array(arr)
implicit none
real, intent(inout) :: arr(:) ! Assumed-shape array
arr = arr * 2.0
end subroutine double_array
File: main_array.f90
program array_interface_demo
implicit none
real :: data(5) = [1.0, 2.0, 3.0, 4.0, 5.0]
! Interface REQUIRED for assumed-shape arrays
interface
subroutine double_array(arr)
real, intent(inout) :: arr(:)
end subroutine double_array
end interface
print *, "Before:", data
call double_array(data)
print *, "After: ", data
end program array_interface_demo
Modules automatically provide interfaces, making your code safer and cleaner:
File: array_module.f90
module array_operations
implicit none
contains
subroutine double_array(arr)
real, intent(inout) :: arr(:)
arr = arr * 2.0
end subroutine double_array
end module array_operations
File: main_module.f90
program module_demo
use array_operations ! Interface is automatic!
implicit none
real :: data(5) = [1.0, 2.0, 3.0, 4.0, 5.0]
print *, "Before:", data
call double_array(data)
print *, "After: ", data
end program module_demo
Real-world Fortran projects typically span multiple files. Here's how to organize and compile them.
module math_module
implicit none
private
public :: factorial, fibonacci, is_prime, gcd
contains
integer function factorial(n)
integer, intent(in) :: n
integer :: i
factorial = 1
do i = 2, n
factorial = factorial * i
end do
end function factorial
integer function fibonacci(n)
integer, intent(in) :: n
integer :: i, prev, curr, next
if (n <= 1) then
fibonacci = n
return
end if
prev = 0
curr = 1
do i = 2, n
next = prev + curr
prev = curr
curr = next
end do
fibonacci = curr
end function fibonacci
logical function is_prime(n)
integer, intent(in) :: n
integer :: i
if (n < 2) then
is_prime = .false.
return
end if
if (n == 2) then
is_prime = .true.
return
end if
if (mod(n, 2) == 0) then
is_prime = .false.
return
end if
do i = 3, int(sqrt(real(n))), 2
if (mod(n, i) == 0) then
is_prime = .false.
return
end if
end do
is_prime = .true.
end function is_prime
integer function gcd(a, b)
integer, intent(in) :: a, b
integer :: x, y, temp
x = abs(a)
y = abs(b)
do while (y /= 0)
temp = y
y = mod(x, y)
x = temp
end do
gcd = x
end function gcd
end module math_module
module io_module
implicit none
private
public :: print_header, print_separator, get_integer_input
contains
subroutine print_header(title)
character(len=*), intent(in) :: title
print *, ""
call print_separator()
print '(A)', " " // trim(title)
call print_separator()
end subroutine print_header
subroutine print_separator()
print '(A)', "=========================================="
end subroutine print_separator
subroutine get_integer_input(prompt, value, min_val, max_val)
character(len=*), intent(in) :: prompt
integer, intent(out) :: value
integer, intent(in), optional :: min_val, max_val
integer :: ios
do
print '(A)', prompt
read(*, *, iostat=ios) value
if (ios /= 0) then
print *, "Invalid input. Please enter an integer."
cycle
end if
if (present(min_val)) then
if (value < min_val) then
print '(A, I0)', "Value must be at least ", min_val
cycle
end if
end if
if (present(max_val)) then
if (value > max_val) then
print '(A, I0)', "Value must be at most ", max_val
cycle
end if
end if
exit
end do
end subroutine get_integer_input
end module io_module
program multi_file_demo
use math_module
use io_module
implicit none
integer :: choice, n, a, b
call print_header("Math Functions Demo")
print *, "Choose a function:"
print *, " 1. Factorial"
print *, " 2. Fibonacci"
print *, " 3. Prime Check"
print *, " 4. GCD"
print *, ""
call get_integer_input("Enter choice (1-4): ", choice, 1, 4)
select case (choice)
case (1)
call get_integer_input("Enter n: ", n, 0, 12)
print '(A, I0, A, I0)', "Factorial(", n, ") = ", factorial(n)
case (2)
call get_integer_input("Enter n: ", n, 0, 40)
print '(A, I0, A, I0)', "Fibonacci(", n, ") = ", fibonacci(n)
case (3)
call get_integer_input("Enter n: ", n, 1)
if (is_prime(n)) then
print '(I0, A)', n, " is prime"
else
print '(I0, A)', n, " is not prime"
end if
case (4)
call get_integer_input("Enter first number: ", a)
call get_integer_input("Enter second number: ", b)
print '(A, I0, A, I0, A, I0)', "GCD(", a, ", ", b, ") = ", gcd(a, b)
end select
call print_separator()
end program multi_file_demo
Option 1: All at once
gfortran math_module.f90 io_module.f90 main.f90 -o math_demo
Option 2: Separate compilation (better for large projects)
# Compile modules first (creates .mod files)
gfortran -c math_module.f90
gfortran -c io_module.f90
# Compile main program
gfortran -c main.f90
# Link everything together
gfortran math_module.o io_module.o main.o -o math_demo
.mod files that contain interface information.
FC = gfortran
FFLAGS = -Wall -g
OBJS = math_module.o io_module.o main.o
math_demo: $(OBJS)
$(FC) $(FFLAGS) -o $@ $(OBJS)
math_module.o: math_module.f90
$(FC) $(FFLAGS) -c __CODE_BLOCK_0__lt;
io_module.o: io_module.f90
$(FC) $(FFLAGS) -c __CODE_BLOCK_0__lt;
main.o: main.f90 math_module.o io_module.o
$(FC) $(FFLAGS) -c __CODE_BLOCK_0__lt;
clean:
rm -f *.o *.mod math_demo
This example puts everything in a single file using contains:
program all_in_one
implicit none
real, allocatable :: data(:)
real :: mean_val, std_val, min_val, max_val
integer :: n, i
! Get array size from user
print *, "Enter number of data points: "
read *, n
if (n < 1) then
print *, "Error: Need at least 1 data point"
stop
end if
! Allocate and fill array
allocate(data(n))
print *, "Enter ", n, " values:"
do i = 1, n
print '(A, I0, A)', " Value ", i, ": "
read *, data(i)
end do
! Calculate statistics using internal subprograms
mean_val = calculate_mean(data)
std_val = calculate_std_dev(data, mean_val)
call find_min_max(data, min_val, max_val)
! Display results
call print_results(data, mean_val, std_val, min_val, max_val)
! Clean up
deallocate(data)
contains
! FUNCTION: Calculate mean of an array
real function calculate_mean(arr)
real, intent(in) :: arr(:)
calculate_mean = sum(arr) / real(size(arr))
end function calculate_mean
! FUNCTION: Calculate standard deviation
real function calculate_std_dev(arr, mean_val)
real, intent(in) :: arr(:)
real, intent(in) :: mean_val
real :: sum_sq
integer :: i
sum_sq = 0.0
do i = 1, size(arr)
sum_sq = sum_sq + (arr(i) - mean_val)**2
end do
calculate_std_dev = sqrt(sum_sq / real(size(arr)))
end function calculate_std_dev
! SUBROUTINE: Find minimum and maximum values
subroutine find_min_max(arr, min_val, max_val)
real, intent(in) :: arr(:)
real, intent(out) :: min_val, max_val
min_val = minval(arr)
max_val = maxval(arr)
end subroutine find_min_max
! SUBROUTINE: Print formatted results
subroutine print_results(arr, mean_val, std_val, min_val, max_val)
real, intent(in) :: arr(:)
real, intent(in) :: mean_val, std_val, min_val, max_val
print *, ""
print *, "========== RESULTS =========="
print '(A, I0)', " Data points: ", size(arr)
print '(A, F12.4)', " Mean: ", mean_val
print '(A, F12.4)', " Std Dev: ", std_val
print '(A, F12.4)', " Minimum: ", min_val
print '(A, F12.4)', " Maximum: ", max_val
print '(A, F12.4)', " Range: ", max_val - min_val
print *, "============================="
end subroutine print_results
end program all_in_one
File: stats_module.f90
module stats_module
implicit none
private
public :: calculate_mean, calculate_std_dev, find_min_max, print_results
contains
real function calculate_mean(arr)
real, intent(in) :: arr(:)
calculate_mean = sum(arr) / real(size(arr))
end function calculate_mean
real function calculate_std_dev(arr, mean_val)
real, intent(in) :: arr(:)
real, intent(in) :: mean_val
real :: sum_sq
integer :: i
sum_sq = 0.0
do i = 1, size(arr)
sum_sq = sum_sq + (arr(i) - mean_val)**2
end do
calculate_std_dev = sqrt(sum_sq / real(size(arr)))
end function calculate_std_dev
subroutine find_min_max(arr, min_val, max_val)
real, intent(in) :: arr(:)
real, intent(out) :: min_val, max_val
min_val = minval(arr)
max_val = maxval(arr)
end subroutine find_min_max
subroutine print_results(arr, mean_val, std_val, min_val, max_val)
real, intent(in) :: arr(:)
real, intent(in) :: mean_val, std_val, min_val, max_val
print *, ""
print *, "========== RESULTS =========="
print '(A, I0)', " Data points: ", size(arr)
print '(A, F12.4)', " Mean: ", mean_val
print '(A, F12.4)', " Std Dev: ", std_val
print '(A, F12.4)', " Minimum: ", min_val
print '(A, F12.4)', " Maximum: ", max_val
print '(A, F12.4)', " Range: ", max_val - min_val
print *, "============================="
end subroutine print_results
end module stats_module
File: main_separate.f90
program main_separate
use stats_module
implicit none
real, allocatable :: data(:)
real :: mean_val, std_val, min_val, max_val
integer :: n, i
print *, "Enter number of data points: "
read *, n
if (n < 1) then
print *, "Error: Need at least 1 data point"
stop
end if
allocate(data(n))
print *, "Enter ", n, " values:"
do i = 1, n
print '(A, I0, A)', " Value ", i, ": "
read *, data(i)
end do
! Call module functions and subroutines
mean_val = calculate_mean(data)
std_val = calculate_std_dev(data, mean_val)
call find_min_max(data, min_val, max_val)
call print_results(data, mean_val, std_val, min_val, max_val)
deallocate(data)
end program main_separate
Compile with:
gfortran stats_module.f90 main_separate.f90 -o stats_program
| Task | Syntax |
|---|---|
| Define a function | real function name(args) or function name(args) result(res) |
| Define a subroutine | subroutine name(args) |
| Call a subroutine | call subroutine_name(args) |
| Input-only argument | type, intent(in) :: arg |
| Output-only argument | type, intent(out) :: arg |
| Input/output argument | type, intent(inout) :: arg |
| Assumed-shape array | real, intent(in) :: arr(:) |
| Define a module | module name ... contains ... end module name |
| Use a module | use module_name |
| Selective import | use module_name, only: item1, item2 |
| Rename on import | use module_name, only: new_name => old_name |
| Interface block | interface ... end interface |
| Internal subprograms | After contains in program |
| Make private | private or private :: item |
| Make public | public or public :: item |
Write a Fortran program that implements a vector mathematics library using both functions and subroutines. Your implementation should demonstrate modular design and proper use of interfaces.
vector_module.f90)Create a module named vector_ops that provides the following:
Functions (return a single value):
vector_magnitude(v) — Returns the magnitude (length) of a vectordot_product_custom(v1, v2) — Returns the dot product of two vectors (name it dot_product_custom to avoid conflict with Fortran's intrinsic)vector_angle(v1, v2) — Returns the angle between two vectors in degreesSubroutines (modify arguments):
vector_add(v1, v2, result) — Adds two vectors, stores result in third argumentvector_scale(v, scalar, result) — Multiplies vector by scalar, stores resultvector_normalize(v, result) — Creates a unit vector in the same directionvector_cross_product(v1, v2, result) — Computes cross product (for 3D vectors only)vector_main.f90)Write a main program that:
vector_ops moduleCreate an additional file (external_func.f90) containing a single external function:
real function vector_sum(v, n)
! Returns the sum of all elements in vector v
Modify your main program to also call this external function, using a proper interface block to declare it.
=== Vector Operations Demo ===
Vector A: [ 3.00, 4.00, 0.00]
Vector B: [ 1.00, 0.00, 0.00]
FUNCTION RESULTS:
Magnitude of A: 5.0000
Magnitude of B: 1.0000
Dot product A·B: 3.0000
Angle between A and B: 53.1301 degrees
SUBROUTINE RESULTS:
A + B = [ 4.00, 4.00, 0.00]
A × 2 = [ 6.00, 8.00, 0.00]
Normalized A = [ 0.60, 0.80, 0.00]
3D Cross Product Demo:
Vector C: [ 1.00, 0.00, 0.00]
Vector D: [ 0.00, 1.00, 0.00]
C × D = [ 0.00, 0.00, 1.00]
EXTERNAL FUNCTION (with interface):
Sum of A elements: 7.0000
Memory deallocated successfully.
\[ |\vec{v}| = \sqrt{v_1^2 + v_2^2 + v_3^2} \]
\[ \vec{v_1} \cdot \vec{v_2} = v_{1x}v_{2x} + v_{1y}v_{2y} + v_{1z}v_{2z} \]
\[ \theta = \arccos\left(\frac{\vec{v_1} \cdot \vec{v_2}}{|\vec{v_1}| \cdot |\vec{v_2}|}\right) \]
\[ \vec{v_1} \times \vec{v_2} = \begin{pmatrix} v_{1y}v_{2z} - v_{1z}v_{2y} \\ v_{1z}v_{2x} - v_{1x}v_{2z} \\ v_{1x}v_{2y} - v_{1y}v_{2x} \end{pmatrix} \]
intent attributes on all argumentsarr(:)) for flexibilitydegrees = radians * 180.0 / PI# Compile the module first
gfortran -c vector_module.f90
# Compile the external function
gfortran -c external_func.f90
# Compile and link main program
gfortran vector_module.o external_func.o vector_main.f90 -o vector_demo
# Run
./vector_demo