https://www.youtube.com/watch?v=__2UgFNYgf8
## 1. Compilers
gfortran - opensourced
ifort - Intel's compiler
## 2. Column rules
In old fortran (Fortran77?) First 6 columns in each line is reserved. Column 1-5 are for IDs, Column 6 for continuation mark
## 3. Variables
put `implicit none` on top of the program
variables are case in-sensitive
1. `real, parameter:: PI =3.1415`
2. `real:: r_num1 =0.0`
3. `double precision:: dbl_num = 1.111111111111111111d+0` - d+0 is needed in doubles
4. `integer:: i_num = 1`
5. `logical:: can_vote = .true.`
6. `character (len=10):: month`
7. `complex:: com_num =(2.0, 4.0)`
variables need to be declared on top
## 4. Print Statement
the syntax of print statement is
```fortran
print f [, iolist]
! print 3 integers in one line each integer having 5 characters width
print "(3i5)", 7, 6, 8
! the width for the float also should include the decimal places
print "(2f8.5)", 3.1415, 1.234
! exponential notation
print "(e10.3)", 123.456 ! will output 0.123E+03
```
where f is format identifier
and iolist is list of variables, arrays, strings, etc to print
### 4.1. format identifier
\* - list directed
Fortran format can be described in following notation
w: the number of positions to be used
m: the minimum number of positions to be used
d: the number of digits to the right of the decimal point
e: the number of digits in the exponent part
## 5. Math operators and functions
what you expect....
** is power
trigonometric functions use radians not degrees (sin, cos, tan, etc)
log(x) is natural log
not equal to is /=
```fortran
real(8) :: x = 2.7182818284590451_8
complex :: z = (1.0, 2.0)
x = log(x) ! will yield (approximately) 1
z = log(z)
print *, "x value should be 1 ", x
print *, "z value is??", z
```
## 6. Conditionals
logical operators
.and.
.or.
.not.
.true.
etc..
```fortran
integer :: age = 6
if ((age >= 5) .and. (age<=6)) then
print *, "Kintergarden"
else if ((age >= 6) .and. (age<=7)) then
print*, "First Grade"
else
print*, "No School"
end if
```
```fortran
integer :: age = 6
select case(age)
case (5)
print *, "Kintergarden"
case(6:13)
print *, "elementary school"
case default
print *, "no school"
end select
```
## 7. Looping
### 7.1. Do loop
``` fortran
integer :: n=0, m=1
integer :: secret_num = 7
! print only even numbers
do while (m < 20)
if (mod(m,2) == 0) then
print "(i1)", m
m = m + 1
! cyle is continue in python
cycle
end if
m = m + 1
if (m >= 10) then
! exit is break in python
exit
end if
end do
```
## 8. Arrays
```fortran
integer :: n,m,x,y
integer :: num_vals = 0
! Arrays
integer, dimension(1:5) :: a1, a2, a3
real, dimension(1:50) :: aR1
! multi-dimensional
integer, dimension(5,5) :: a4
integer, dimension(:), allocatable :: a5
integer, dimension(1:9) :: a6 = (/ 1, 2, 3, 4, 5 ,6, 7, 8, 9 /)
integer, dimension(1:3, 1:3) :: a7
! set and retrieve values
a1(1) = 5
print"(i1)", a1(1)
! cycle through a range of 1 to 5
do n = 1, 5
a1(n)= n
end do
! print them
do n = 1, 5
print "(i1)", a1(n)
end do
print "(3i2)", a1(1:3)
print "(2i2)", a1(1:3:2)
! asssign values to multi-dimensional array
do n = 1,5
do m = 1,5
a4(n,m) = n
end do
end do
! implied do loop
do n =1,5
print "(5i1)", (a4(n,m), m = 1,5)
end do
! size and dimensions
print "(i2)", rank(a4)
print "(i2)", shape(a2)
print "(i2)", size(a2)
print "(i2)", count(a2)
print "(i2)", maxval(a2)
print "(i2)", minval(a2)
! dynamic allocaiton
print *, "Size of array? "
read *, num_vals
allocate(a5(1:numvals))
do n =1, numvals
a5(n) = n
end do
! reshape an array
a7 = reshape(a6, (/ 3,3 /))
! check if values are equal
print "(l1)", all(a1 == a2, 1)
```
## 9. Strings
```fortran
character (len=30) :: str1 = "I'm a string"
character (len=30) :: str2 = " that is now longer"
character (len=30) :: str3
! trim and concatenate the two strings
str3 = trim(str1) // trim(str2)
! to trim only left or right side use adjustl() or adjustr() instead of trim()
print *, str3
print *, str3(1:3)
print "(a9, i1)", "Index at ", index(str1, "string")
print *, len(str)
```
## 10. Structures
```fortran
! declare customer
type Customer
character (len=40) :: name
integer :: age
real :: balance
end type Customer
! create an array of customers
type(Customer), dimension(5) :: customers
integer :: n
! populate values for a customer
type(Customer) :: cust1
cust1%name = "Sally Smith"
cust1%age = 34
cust1%balance = 320.45
customers(1) = cust1
customers(2)%name = "Tom May"
customers(2)%age = 42
customers(2)%balance = 229.75
do n = 1,2
print *, customers(n)
```
## 11. Functions
```fortran
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 + ? = ", get_sum2(5)
contains
integer function get_sum(n1, n2)
inplicit none
integer :: n1, n2, sum
sum = n1 + n2
end function get sum
function get_sum2(n1,n2) result(sum)
implicit none
! this is to set it up so the variable values that were passed inside cannot be changed
integer, intent(in) :: n1, n2
integer :: sum
sum = n1 + n2
end function get_sum2
! function cannot change input variable
pure function get_sum3(n1, n2) result(sum)
implicit none
integer, intent(in) :: n1
! if the argument is optional
integer, intent(in), optional :: n2
integer :: sum
if (present (n2)) then
sum = n1 + n2
else
sum = n1 + 1
end if
end function get_sum3
```
## 12. Subroutines
Subroutines don't return values. They modify the variables that are passed to them. Its like a macro. you need to use "call" to call a subroutine but functions are called normally.
```fortran
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
! tells we are going to return two values
integer, intent(out) :: plus1, plus2
plus1 = n + 1
plus2 = n + 1
end subroutine plus_two
```
## 13. Variable scope
Objects (variables, etc.) declared in main program/module can be accessed and modified (!!!) by any function/subroutine contained by them. name your variables carefully.
![[fortran_scope.png]]
[Scope in Fortran 90 (utah.edu)](https://www.inscc.utah.edu/~krueger/6150/Scope_Fortran_90.pdf)
### 13.1. Intent
Intent is a useful tool. If you set a variable to be `intent(in)` it cannot be modified in the program
`intent(out)` seems more like a documentation thing than having actual purpose as a guardrail.
`intent(inout)` is a thing. Hope you never have to see it again.
### 13.2. Module
use `public` and `private` to declare variables with intention. all variables declared in module scope are public by default
### 13.3. Examples
Be careful of variables in subroutines and functions. They can
```fortran
integer :: i = 1, p1, p2
integer :: j = 5
call plus_two(i, p1, p2)
print "(i1, /, i1, / , i1)", i , p1, p2
contains
subroutine plus_two(n, plus1, plus2)
integer, intent(in) :: n
! tells we are going to return two values
integer, intent(out) :: plus1, plus2
plus1 = n + j
plus2 = n + j
end subroutine plus_two
```
```fortran
integer :: i = 1, p1, p2
integer :: j = 5
p1 = plus_one(i)
print "(i1, /, i1 )", i , p1
print "(i1)", j ! will return 6 and not 5
contains
function plus_one(n) result(plus1)
integer, intent(in) :: n
integer :: plus1
j = j + 1 ! this will modify the global variable
plus1 = n + j
end function plus_one
```
## 14. Procedures
https://annefou.github.io/Fortran/modules/modules.html
In Fortran, "procedures" is an umbrella term that mean "functions" or/and "subroutines".
Functions and subroutines are very similar except a function returns a value while a subroutine doesn't.
There are 4 ways to define procedures:
- Internal procedures are defined within the program structure (CONTAINS)
- External procedures are independently declared and may be on another language(EXTERNAL) (*dont use them modules are better*)
- Module procedure are defined in a module
- Procedures defined as part of an object
## 15. Modules
### 15.1. Function overloading example
mult_mod.f90 contains
```fortran
module mult_mod
implicit none
! private variables can be accessed only within the module
private
! public variables can be accessed outside
pubic :: mult
interface mult
procedure mult_real, mult_int
end interface mult
contains
real function mult_real(n1,n2)
real, intetn(in) :: n1, n2
real :: product
product = n1 * n2
end function mult_real
integer function mult_int(n1,n2)
integer, intetn(in) :: n1, n2
integer :: product
product = n1 * n2
end function mult_int
end module mult_mod
```
first.f90 contains
```fortran
use mult_mod
implicit none
real :: r_ans
print "(a8,i2)", "5 * 4 = ", mult(5,4)
r_ans = mult(5.3, 4.4)
print "(a12, f 6.2)", "5.3 * 4.4 = ", rans
```
to run this
```powershell
ifort mult_mod.f90 first.f90 /exe:first.exe
```
### 15.2. Another simple example
first.f90 file contains
```fortran
use shape
implicit none
call set_shape(10.4, 20.5)
call get_area()
```
shape.f90 file contains
```fortran
module shape
implicit none
real, private :: height =1
real, private :: width =1
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
```
to run this
```powershell
ifort shape.f90 first.f90 /exe:first.exe
```
### 15.3. Inherit from one module to another
shape_mod.f90 file contains
```fortran
module shape_mod
implicit none
! this is like a class definition
! set supertype (which is shape) as abstract so that it can be inherited from
type, abstract :: shape_m
real :: x, y
contains
! procedure means it is defined as part of an object it can be a function or subroutine
! deferred means it will be defined in all of the subtypes
procedure(shape_area), deferred :: get_area
end type shape_m
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 contains
```fortran
module triangle_mod
use shape_mod
implicit none
type, extends(shape_m), public :: triangle_m
contains
procedure :: get_area
end type triangle_m
contains
function get_area(this) result(area)
class(triangle_m) :: this
real :: area
area = 0.5 * this%x * this%y
end function get_area
end module triangle_mod
```
first.f90 file now contains
```fortran
use shape_mod
use triangle_mod
implicit none
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()
```
to run this
```powershell
ifort shape_mod.f90 triangle_mod.f90 first.f90 /exe:first.exe
```
## 16. Pointers
```fortran
integer, pointer :: ptr1, ptr2
! pointer to an array integer
integer, pointer, dimension(:) :: a_ptr1
! target whose value will change as the pointers value changes
integer, target :: target1
! allocate space for the pointer
allocate(ptr1)
! assign a value in the memory location
ptr1 = 5
print "(a5,i1)", "ptr1", ptr1
!-- another way --!
! associate a pointer with its target
ptr2 => target1
! change the value of the target
ptr2 = 1
ptr2 = ptr2 + 2
print "(a5, i1)", "ptr2", ptr2
print "(a7, i1)", "target1", target1
! dereference a pointer from its target
nullify(ptr2)
! deallocate storage
deallocate(ptr1)
```
## 17. File I/O
```fortran
character (len=100) :: str1 = "I'm a string"
character (len=100) :: str2
! if err_status is not zero then a error has occured
integer :: err_status
character(256) :: err_iomsg
! 10 is unit number which must be unique to every file and it must be above 9
! status can be new, old, scratch. scratch means delete after using. new is a new file. old is an existing file
open(10, file="data.dat", status="new", iostat=err_status, iomsg=err_iomsg)
if (err_stat /=0) then
write(*,*) "Error", trim(err,io_msg)
stop
end if
write(10, "(A)") str
close(10)
open(11, file="data.dat", status="old")
read(11, "(A)") str2
write(*, "(A)") trim(str2)
! delete the file after closing it
close(11, status="Delete")
```