Fortran Tutorial

Fortran TutorialIn this one video I condense most everything you’d learn about Fortran in a 400 page book into one video. I cover User Input, Variables, Data Types, Print, Formatted Output, Math Operators, Random Numbers, Math Functions, Conditionals, If / Else, Select, Looping, While, Cycle, Exit, Arrays, Format, Strings, Structures, Functions, Optional Arguments, Recursive Functions, Modules, Overloading, Subroutines, Pointers, File I/O, Modules, Class, Interface and more. All of the code and a transcript follows below.

If you like videos like this, consider donating $1, or simply turn off Ad Blocking software. Either allows me to continue making videos for free.

Cheat Sheet / Transcript

! Name of program
! The f90 extension can be used for
! Fortran 2003 code
! Compile like this
! gfortran fortrantut.f90
program fortrantut

  ! A module we will use later compiled
  ! like this
  ! gfortran -c mult_mod.f90  fortrantut.f90
  ! Run like this
  ! gfortran mult_mod.o  fortrantut.o
  ! use mult_mod
  ! use shape
  use shape_mod
  use triangle_mod

  ! Forces you to declare all variables
  implicit none

  ! Create a variable that can hold up to
  ! 20 characters
  character*20 :: name
  
  ! Print a string to the screen
  ! * use default formatting
  print *, "What's your name : "
  
  ! Receive input up to a whitespace
  ! or newline
  read *, name
  
  ! Output the result
  print * , "Hello ", name

  character (len = 20) :: f_name, l_name
  print *, "Enter Name : "
  ! Read 2 values separated by a space
  read *, f_name, l_name
  ! Trim extra whitespace
  print *, "Hello ", trim(f_name), " ", trim(l_name)

  ! ----- VARIABLES / DATA TYPES -----
  ! Variables must start with a letter
  ! and then letters, numbers, _
  ! Variables are case insensitive

  ! Declare a constant that's value
  ! can't change
  real, parameter :: PI = 3.1415
  
  ! Numbers with decimals (floats)
  ! You can assign a value or leave undefined
  real :: r_num1 = 0.0, r_num2 = 0.0
  ! Doubles are accurate to 15 decimals
  double precision :: dbl_num = 1.1111111111111111d+0
  ! Numbers without decimals (whole numbers)
  integer :: i_num1 = 0, i_num2 = 0
  ! Boolean type
  logical :: can_vote = .true.
  ! Another way to declare a string
  character (len = 10) :: month
  ! Complex TYPES
  complex :: com_num = (2.0, 4.0)
  
  ! Get largest value for data types
  print *, "Biggest Real ", huge(r_num1)
  print *, "Biggest Int ", huge(i_num1)

  ! Get smallest value for data types
  print *, "Smallest Real ", tiny(r_num1)
  print *, "Smallest Int ", tiny(i_num1)

  ! Kind returns the number of bytes for each type
  print "(a4, i1)", "Int ", kind(i_num1)
  print "(a5, i1)", "Real ", kind(r_num1)
  print "(a7, i1)", "Double ", kind(dbl_num)
  print "(a8, i1)", "Logical ", kind(can_vote)

  ! ! ----- FORMATTED OUTPUT WITH PRINT -----
  ! character(len=5) :: i_char
  ! Integers are right justified by default
  print *, "A Number ", 10
  
  ! Integers are formatted like this RiW
  ! R : Number of times to use what follows per line
  ! W : Width to take up for each value
  print "(3i5)", 7, 6, 8
  print "(i5)", 7, 6, 8
  
  ! Floats are formatted like RfW.D
  ! R & W : Same as above
  ! D : Decimal places to show
  print "(2f8.5)", 3.1415, 1.234
  
  ! Characters & Strings are formatted RaW
  ! / Adds a newline
  print "(/, 2a8)", "Name", "Age"
  
  ! Exponential Notation ReW.D
  print "(e10.3)",123.456
  
  ! Use multiple types
  print "(a5,i2)", "I am ", 43
  
  ! Left justify Numbers
  ! Convert int 10 into a string
  write (i_char, "(i5)") 10
  
  ! Print formatted output left justified
  print "(a,a)", "A Number ", adjustl(i_char)

  ! ----- MATH OPERATORS -----
  real :: float_num = 1.111111111111111
  real :: float_num2 = 1.111111111111111
  double precision :: dbl_num = 1.1111111111111111d+0
  double precision :: dbl_num2 = 1.1111111111111111d+0
  real :: rand(1)
  integer :: low = 1, high = 10
  
  print "(a8,i1)", "5 + 4 = ", (5 + 4)
  print "(a8,i1)", "5 - 4 = ", (5 - 4)
  print "(a8,i2)", "5 * 4 = ", (5 * 4)
  print "(a8,i1)", "5 / 4 = ", (5 / 4)
  ! Modulus
  print "(a8,i1)", "5 % 4 = ", mod(5,4)
  ! Exponentiation
  print "(a7,i3)", "5**4 = ", (5**4)
  
  ! You get 6 digits of precision by default
  print "(f17.15)", float_num + float_num2
  
  ! Doubles are accurate to 15 digits
  print "(f18.16)", dbl_num + dbl_num2

  Generate random values between 1 and 10
  call random_number(rand)
  print "(i2)", low + floor((high + 1 - low)*rand)

  ! ----- Math Functions -----
  print "(a10,i1)", "ABS(-1) = ", ABS(-1)
  print "(a11,f3.1)", "SQRT(81) = ", SQRT(81.0)
  print "(a9,f7.5)", "EXP(1) = ", EXP(1.0)
  print "(a12,f7.5)", "LOG(2.71) = ", LOG(2.71)
  print "(a12,i1)", "INT(2.71) = ", INT(2.71)
  print "(a13,i1)", "NINT(2.71) = ", NINT(2.71)
  print "(a14,i1)", "FLOOR(2.71) = ", FLOOR(2.71)
  print "(a15,f3.1)", "MAX(2.7,3.4) = ", MAX(2.7,3.4)
  print "(a15,f3.1)", "MIN(2.7,3.4) = ", MIN(2.7,3.4)
  ! Trig functions use radians
  print "(a14,f3.1)", "SIN(1.5708) = ", SIN(1.5708)
  print "(a14,f3.1)", "COS(1.5708) = ", COS(1.5708)
  print "(a14,f3.1)", "TAN(1.5708) = ", TAN(1.5708)
  print "(a10,f3.1)", "ASIN(0) = ", ASIN(0.0)
  print "(a10,f3.1)", "ACOS(0) = ", ACOS(0.0)
  print "(a10,f3.1)", "ATAN(0) = ", ATAN(0.0)

  ! ----- CONDITIONALS -----
  ! Relational Operators : == /= > < >= <=
  ! Logical Operators : .and. .or. .not.

  ! If, else if, else
  integer :: age = 16
  if ((age >= 5) .and. (age <= 6)) then
    print *, "Kindergarten"
  else if ((age >= 7) .and. (age <= 13)) then
    print *, "Middle School"
  else if ((age >= 14) .and. (age <= 18)) then
    print *, "High School"
  else
    print *, "Stay Home"
  end if
  
  print *, .true. .or. .false.
  print *, .not. .true.
  print *, 5 /= 9
  
  ! Can be used with letters
  print *, "a" < "b"
  
  ! Select
  select case (age)
  case (5)
    print *, "Kindergarten"
  case (6:13)
    print *, "Middle School"
  case (14,15,16,17,18)
    print *, "High School"
  case default
    print *, "Stay Home"
  end select

  ! ----- LOOPING -----
  integer :: n = 0, m = 1
  integer :: secret_num = 7
  
  ! Start, Finish, Step
  do n = 1, 10, 2
    print "(i1)", n
  end do
  
  ! Exit & Cycle
  ! Print only evens
  do while (m < 20)
    if (MOD(m,2) == 0) then
      print "(i1)", m
      m = m + 1
      ! Jumps back to beginning of loop
      cycle
    end if
    m = m + 1
    if (m >= 10) then
      ! Exits the loop all together
      exit
    end if
  end do
  
  ! Continue looping while a condition is true
  do while (n /= secret_num)
    print *, "What's your guess "
    read *, n
  end do
  print *, "You guessed it!"

  ! ----- ARRAYS -----
  ! Create ARRAY
  integer, dimension(1:5) :: a1, a2, a3
  real, dimension(1:50) :: aR1
  ! Create multidimensional array (Matrix)
  integer, dimension(5,5) :: a4
  integer :: n, m, x, y

  Define an array thats size is determined
  at run time
  integer, dimension(:), allocatable :: a5
  integer :: num_vals = 0
  
  integer, dimension(1:9) :: a6 = (/ 1,2,3,4,5,6,7,8,9 /)
  integer, dimension(1:3,1:3) :: a7
  
  ! Assign values (Starts at index 1)
  a1(1) = 5
  ! Retrieve value
  print "(i1)", a1(1)
  
  ! Assign values with a loop
  do n = 1,5
    a1(n) = n
  end do
  do n = 1,5
    print "(i1)", a1(n)
  end do
  
  ! Get a range
  print "(3i2)", a1(1:3)
  
  ! Get a range with an increment
  print "(2i2)", a1(1:3:2)
  
  ! Assign values to a multidimensional array
  do n = 1,5
    do m = 1, 5
      a4(n,m) = n
    end do
  end do
  do n = 1,5
    do m = 1, 5
      print "(i1,a1,i1,a3,i1)", n, " ", m, " : ", a4(n,m)
    end do
  end do
  
  ! Use an implied do loop to print each row
  ! on one line
  do n = 1,5
    print "(5i1)", ( a4(n,m), m = 1,5 )
  end do
  
  ! Get size
  print "(i2)", Size(a1)
  print "(i2)", Size(a4)
  
  ! Number of dimensions
  print "(i2)", Rank(a4)
  
  ! Elements in each dimension
  print "(i2)", Shape(a4)
  
  ! Define array size at run time
  print *, "Size of array? "
  read *, num_vals
  allocate(a5(1:num_vals))
  do n = 1,num_vals
    a5(n) = n
  end do
  do n = 1,num_vals
    print "(i1)", a5(n)
  end do
  
  ! Change all values in array
  a2 = (/1,2,3,6,7/)
  
  ! Implied do loop
  print "(5i1)", ( a2(m), m = 1,5 )
  
  ! Reshape the ARRAY from 1x9 t0 3x3
  a7 = reshape(a6, (/ 3, 3 /))
  
  ! Check if values are equal across
  ! the 1 dimension
  print "(l1)", all(a1==a2, 1)
  
  ! Are any equal?
  print "(l1)", any(a1==a2, 1)
  
  ! How many are equal
  print "(i1)", count(a1==a2, 1)
  
  ! Get min and max value
  print "(i1)", maxval(a1)
  print "(i1)", minval(a1)
  
  ! Get product and sum
  print "(i3)", product(a1)
  print "(i2)", sum(a1)

  ! ----- FORMAT -----
  ! The format statement has a numbered
  ! label. You pass values to it that will
  ! fit into the designated formatting
  integer :: num
  integer :: cups
  real :: liters
  real :: quarts
  
  ! Print values 1 - 12 * 7
  do num = 1,12
    print 100, num, num * 7
  
    ! I designates an integer along with
    ! total space with values right justified
    100 format(I2,' * 7 = ',I3)
  end do
  
  ! / Adds a newline
  print "(/a18)", "Cups Liters Quarts"
  do cups = 1, 10
    liters = cups * .236
    quarts = cups * .208
    print 200 , cups,liters,quarts
  
    ! x defines spaces f is for floats
    200 format(' ',i3, 2x, f5.3, 2x, f5.3)
  end do

  ! ----- STRINGS -----
  ! Strings are character arrays
  character (len=30) :: str = "I'm a string"
  character (len=30) :: str2 = " that is longer"
  character (len=30) :: str3
  
  ! Join strings that have been trimmed of
  ! whitespace
  ! You can also trim right (adjustr) and
  ! left (adjustl)
  str3 = trim(str) // trim(str2)
  print *, str3
  
  ! Get a substring
  print *, str3(1:3)
  
  ! Find the index of a substring
  print "(a9,i1)", "Index at ", index(str, "string")
  
  ! Get size
  print *, len(str)
  
  ! Get number of items separated by a space
  print *, count_items(str)

  ! ----- STRUCTURES -----
  ! You can define custom types which contain
  ! multiple values of different types
  type Customer
    character (len = 40) :: name
    integer :: age
    real :: balance
  end type Customer
  
  type(Customer), dimension(5) :: customers
  
  integer :: n
  
  ! Create a customer
  type(Customer) :: cust1
  
  ! Assign values
  cust1%name = "Sally Smith"
  cust1%age = 34
  cust1%balance = 320.45
  
  ! Assign structure to array
  customers(1) = cust1
  
  ! Assign values independently
  customers(2)%name = "Tom May"
  customers(2)%age = 42
  customers(2)%balance = 229.78
  
  do n = 1, 2
    print *, customers(n)
  end do

  ! ----- FUNCTIONS -----
  ! Functions contain statements that return
  ! a single value

  integer :: ans, ans2
  real :: r_ans
  
  ans = get_sum(5,4)
  print "(a8,i1)", "5 + 4 = ", ans
  print "(a8,i1)", "5 + 4 = ", get_sum2(5,4)
  print "(a8,i1)", "5 + 4 = ", get_sum3(5)
  
  ! Use generic functions in a module that
  ! can work with ints and reals using
  ! the same function
  print "(a8,i2)", "5 * 4 = ", mult(5,4)
  r_ans = mult(5.3,4.4)
  print "(a12,f6.2)", "5.3 * 4.4 = ", r_ans
  
  ! Defines area for functions
  contains
  
    ! Return type, function, name, arguments
    integer function get_sum(n1, n2)
      implicit none
      integer :: n1, n2, sum
  
      ! The last value defined is returned
      sum = n1 + n2
    end function get_sum
  
    ! Define variable to be returned
    function get_sum2(n1, n2) result(sum)
      implicit none
  
      ! Don't allow variable values to change
      integer, intent(in) :: n1, n2
      integer :: sum
      sum = n1 + n2
    end function get_sum2
  
    ! Block functions from changing input
    ! variables with pure
    pure function get_sum3(n1, n2) result(sum)
      implicit none
      integer, intent(in) :: n1
  
      ! Arguments don't need to have a value passed
      integer, intent(in), optional :: n2
      integer :: sum
  
      if(present(n2)) then
        sum = n1 + n2
      else
        sum = n1 + 1
      end if
    end function get_sum3

  ! ----- RECURSIVE FUNCTIONS -----
  ! Recursive functions call themselves
  ! and must be labeled as such in Fortran
  integer :: ans
  ans = factorial(4)
  print "(a15,i3)", "Factorial(4) = ", ans

  ! 1st : result = 4 * factorial(3) = 4 * 6 = 24
  ! 2nd : result = 3 * factorial(2) = 3 * 2 = 6
  ! 3rd : result = 2 * factorial(1) = 2 * 1 = 2

contains

  recursive function factorial(n) result(o)
    integer :: n, o
    if (n == 1) then
      o = 1
    else
      o = n * factorial(n - 1)
    end if
  end function

  ! ----- SUBROUTINES -----
  ! Subroutines can return multiple values

integer :: i = 1, p1, p2
call plus_two(i, p1, p2)
print "(i1,/,i1,/,i1)", i, p1, p2

contains

  subroutine plus_two(n, plus1, plus2)
    integer, intent(in) :: n
    integer, intent(out) :: plus1, plus2 ! Output
    plus1 = n + 1
    plus2 = n + 2
  end subroutine plus_two

  ! ----- POINTERS -----
  ! Declare a pointer to an integer
  integer, pointer :: ptr1, ptr2
  
  ! Pointer to an array
  integer, pointer, dimension(:) :: a_ptr1
  
  ! Declare a target whose value changes
  ! as the pointers value changes
  integer, target :: target1
  
  ! Allocate space for a pointer
  allocate(ptr1)
  ptr1 = 5
  print "(a5,i1)", "ptr1 ", ptr1
  
  ! Associate pointer with target
  ptr2 => target1
  ptr2 = 1
  
  ptr2 = ptr2 + 2
  print "(a5,i1)", "ptr1 ", ptr1
  print "(a5,i1)", "tar1 ", target1
  
  ! Disassociate pointer and target
  nullify(ptr2)
  
  ! Deallocate storage for pointer
  deallocate(ptr1)

  ! ----- FILE I/O -----
  character (len=100) :: str = "I'm a string"
  character (len=100) :: str2
  
  ! If set to anything other then 0 an
  ! error occurred when opening a file
  integer :: err_status
  
  ! Used to catch error messages
  CHARACTER(256) :: err_iomsg
  
  ! Open / Create a FILE
  ! The unit number must be unique for
  ! each file
  ! new (new file), old (exists),
  ! scratch (file deleted after use)
  open(10, file='data.dat', status='new', iostat = err_status, iomsg=err_iomsg)
  if(err_status /= 0) then
    write (*,*) 'Error ', trim(err_iomsg)
  
    ! Stop execution
    Stop
  end if
  
  ! Write string to file
  write (10, '(A)') str
  
  ! Close the file
  close(10)
  
  ! Open to read
  open(11, file='data.dat', status='old')
  
  ! Read from file
  read (11, '(A)') str2
  write (*, '(A)') trim(str2)
  
  ! Either KEEP or DELETE file when closed
  close(11, status="DELETE")

  ! ----- ANOTHER MODULE EXAMPLE -----
  ! Compile
  ! gfortran -c shape.f90  fortrantut.f90
  ! gfortran shape.o  fortrantut.o
  ! ./a.out
  call set_shape(10.5,20.5)
  call get_area()

  ! Compile
  ! gfortran -c shape_mod.f90 triangle_mod.f90  fortrantut.f90
  ! gfortran shape_mod.o triangle_mod.o  fortrantut.o
  type(triangle_m) :: tri
  tri%x = 10
  tri%y = 20
  print "(a3,f5.2)", "X: ", tri%x
  print "(a3,f5.2)", "Y: ", tri%y
  print "(a6,f6.2)", "Area: ", tri%get_area()

! Define the end of the program
end program fortrantut
SHAPE.F90


module shape
  implicit none

    ! private : No access for outside code
    ! public : Can read and write
    real, private :: height = 1
    real, private :: width = 1

    ! Declare your subroutines
    public :: set_shape, get_area

contains
  subroutine set_shape(h, w)
    implicit none
    real, intent(in) :: h, w
    height = h
    width = w
  end subroutine set_shape

  subroutine get_area()
    print *, "Area: ", (height * width)
  end subroutine get_area
end module shape


SHAPE_MOD.F90

! You can derive one type from another
module shape_mod
  implicit none

  ! Set the super type as abstract
  type, abstract :: shape_m

    ! Sub-types receive these variables
    real :: x, y

  contains

    ! This procedure marked as deferred
    ! will be defined in sub-types
    procedure(shape_area), deferred :: get_area
  end type shape_m

  ! Define that we want to return the defined
  ! area for each shape sub-class
  interface
    function shape_area(this) result(area)
      import :: shape_m
      class(shape_m) :: this
      real :: area
    end function shape_area
  end interface
end module shape_mod


TRIANGLE_MOD.F90


module triangle_mod
  ! Import shape_mod and all you inherit
  use shape_mod
  implicit none
  type, extends(shape_m), public :: triangle_m
  contains
    ! Define function you will override
    procedure :: get_area
  end type triangle_m

contains

  ! Refer to this triangle and return the area
  function get_area(this) result(area)
    class(triangle_m) :: this
    real :: area
    area = .5 * this%x * this%y
  end function get_area
end module triangle_mod


MULT_MOD.F90

module mult_mod
  implicit none
  private
  public :: mult

  ! We can define the 2 functions we
  ! will associate with the mult function
  ! depending on the input data types
  interface mult
    procedure mult_real, mult_int
  end interface mult

contains
  real function mult_real( n1,n2 )
    real, intent(in) :: n1,n2
    real :: product
    product = n1 * n2
  end function mult_real

  integer function mult_int( n1,n2 )
    integer, intent(in) :: n1,n2
    integer :: product
    product = n1 * n2
  end function mult_int

end module mult_mod

Leave a Reply

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