diff options
Diffstat (limited to 'fortran.html.markdown')
| -rw-r--r-- | fortran.html.markdown | 457 | 
1 files changed, 457 insertions, 0 deletions
| diff --git a/fortran.html.markdown b/fortran.html.markdown new file mode 100644 index 00000000..316f8c87 --- /dev/null +++ b/fortran.html.markdown @@ -0,0 +1,457 @@ +--- +language: Fortran +contributors: +    - ["Robert Steed", "https://github.com/robochat"] +filename: learnfortran.f90 +--- + +Fortran is one of the oldest computer languages. It was developed in the 1950s +by IBM for numeric calculations (Fortran is an abbreviation of "Formula +Translation"). Despite its age, it is still used for high-performance computing +such as weather prediction. However, the language has changed considerably over +the years, although mostly maintaining backwards compatibility; well known +versions are FORTRAN 77, Fortran 90, Fortran 95, Fortran 2003, Fortran 2008, +Fortran 2018 and Fortran 2023. + +This overview will discuss the features of Fortran 2008 since it is the most +widely implemented of the more recent specifications and the later versions are +largely similar (by comparison FORTRAN 77 is a very different language). + +```fortran + +! This is a comment. + +program example         ! declare a program called example. + +    ! Code can only exist inside programs, functions, subroutines or modules. +    ! Using indentation is not required but it is recommended. + +    ! Declaring Variables +    ! =================== + +    ! All declarations must come before statements and expressions. + +    implicit none       ! prevents dynamic declaration of variables (recommended!) +    ! Implicit none must be redeclared in every function/program/module... + +    ! IMPORTANT - Fortran is case insensitive. +    real z +    REAL Z2 + +    real :: v, x        ! WARNING: default initial values are compiler dependent! +    real :: a = 3, b = 2E12, c = 0.01 +    integer :: i, j, k = 1, m +    real, parameter :: PI = 3.1415926535897931    ! declare a constant. +    logical :: y = .TRUE., n = .FALSE.            ! boolean type. +    complex :: w = (0, 1)                         ! sqrt(-1) +    character(len=3) :: month                     ! string of 3 characters. + +    real :: array(6)                              ! declare an array of 6 reals. +    real, dimension(4) :: arrayb                  ! another way to declare an array. +    integer :: arrayc(-10:10)                     ! an array with a custom index. +    real :: array2d(3, 2)                         ! multidimensional array. + +    ! The '::' separators are not always necessary but are recommended. + +    ! many other variable attributes also exist: +    real, pointer :: p                            ! declare a pointer. + +    integer, parameter :: LP = selected_real_kind(20) +    real(kind=LP) :: d                            ! long precision variable. + +    ! WARNING: initialising variables during declaration causes problems +    ! in functions since this automatically implies the 'save' attribute +    ! whereby values are saved between function calls. In general, separate +    ! declaration and initialisation code except for constants! + +    ! Strings +    ! ======= + +    character :: a_char = 'i' +    character(len=6) :: a_str = "qwerty" +    character(len=30) :: str_b +    character(len=*), parameter :: a_long_str = "This is a long string." +    !can have automatic counting of length using (len=*) but only for constants. + +    str_b = a_str//" keyboard"      ! concatenate strings using // operator. + +    ! Assignment & Arithmetic +    ! ======================= + +    Z = 1                           ! assign to variable z declared above (case insensitive). +    j = 10 + 2 - 3 +    a = 11.54/(2.3*3.1) +    b = 2**3                        ! exponentiation + +    ! Control Flow Statements & Operators +    ! =================================== + +    ! Single-line if statement +    if (z == a) b = 4               ! condition always need surrounding parentheses. + +    if (z /= a) then                ! z not equal to a +        ! Other symbolic comparisons are < > <= >= == /= +        b = 4 +    else if (z .GT. a) then         ! z greater than a +        ! Text equivalents to symbol operators are .LT. .GT. .LE. .GE. .EQ. .NE. +        b = 6 +    else if (z < a) then            ! 'then' must be on this line. +        b = 5                       ! execution block must be on a new line. +    else +        b = 10 +    end if                          ! end statement needs the 'if' (or can use 'endif'). + +    if (.NOT. (x < c .AND. v >= a .OR. z == z)) then    ! boolean operators. +        inner: if (.TRUE.) then     ! can name if-construct. +            b = 1 +        end if inner                ! then must name endif statement. +    end if + +    i = 20 +    select case (i) +    case (0, 1)                     ! cases i == 0 or i == 1 +        j = 0 +    case (2:10)                     ! cases i is 2 to 10 inclusive. +        j = 1 +    case (11:)                      ! all cases where i>=11 +        j = 2 +    case default +        j = 3 +    end select + +    month = 'jan' +    ! Condition can be integer, logical or character type. +    ! Select constructions can also be named. +    monthly:select case(month) +    case ("jan") +        j = 0 +    case default +        j = -1 +    end select monthly + +    do i = 2, 10, 2             ! loops from 2 to 10 (inclusive) in increments of 2. +        innerloop: do j = 1, 3  ! loops can be named too. +            exit                ! quits the loop. +        end do innerloop +        cycle                   ! jump to next loop iteration. +    end do + +    ! Goto statement exists but it is heavily discouraged though. +    goto 10 +    stop 1                      ! stops code immediately (returning specified condition code). +10  j = 201                     ! this line is labeled as line 10 + +    ! Arrays +    ! ====== +    array = (/1, 2, 3, 4, 5, 6/) +    array = [1, 2, 3, 4, 5, 6]  ! using Fortran 2003 notation. +    arrayb = [10.2, 3e3, 0.41, 4e-5] +    array2d = reshape([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], [3, 2]) + +    ! Fortran array indexing starts from 1. +    ! (by default but can be defined differently for specific arrays). +    v = array(1)                ! take first element of array. +    v = array2d(2, 2) + +    print *, array(3:5)         ! print all elements from 3rd to 5th (inclusive). +    print *, array2d(1, :)      ! print first column of 2d array. + +    array = array*3 + 2         ! can apply mathematical expressions to arrays. +    array = array*array         ! array operations occur element-wise. +    ! array = array*array2d     ! these arrays would not be compatible. + +    ! There are many built-in functions that operate on arrays. +    c = dot_product(array, array)   ! this is the dot product. +    ! Use matmul() for matrix maths. +    c = sum(array) +    c = maxval(array) +    print *, minloc(array) +    c = size(array) +    print *, shape(array) +    m = count(array > 0) + +    ! Loop over an array (could have used Product() function normally). +    v = 1 +    do i = 1, size(array) +        v = v*array(i) +    end do + +    ! Conditionally execute element-wise assignments. +    array = [1, 2, 3, 4, 5, 6] +    where (array > 3) +        array = array + 1 +    elsewhere(array == 2) +        array = 1 +    elsewhere +        array = 0 +    end where + +    ! Implied-DO loops are a compact way to create arrays. +    array = [(i, i=1, 6)]       ! creates an array of [1,2,3,4,5,6] +    array = [(i, i=1, 12, 2)]   ! creates an array of [1,3,5,7,9,11] +    array = [(i**2, i=1, 6)]    ! creates an array of [1,4,9,16,25,36] +    array = [(4, 5, i=1, 3)]    ! creates an array of [4,5,4,5,4,5] + +    ! Input/Output +    ! ============ + +    print *, b                  ! print the variable 'b' to the command line + +    ! We can format our printed output. +    print "(I6)", 320           ! prints '   320' +    print "(I6.4)", 3           ! prints '  0003' +    print "(F6.3)", 4.32        ! prints ' 4.320' + +    ! The letter indicates the expected type and the number afterwards gives +    ! the number of characters to use for printing the value. +    ! Letters can be I (integer), F (real), E (engineering format), +    ! L (logical), A (characters) ... +    print "(I3)", 3200          ! print '***' since the number doesn't fit. + +    ! we can have multiple format specifications. +    print "(I5,F6.2,E6.2)", 120, 43.41, 43.41 +    print "(3I5)", 10, 20, 30                       ! 3 repeats of integers (field width = 5). +    print "(2(I5,F6.2))", 120, 43.42, 340, 65.3     ! repeated grouping of formats. + +    ! We can also read input from the terminal. +    read (*, *) v +    read (*, "(2F6.2)") v, x                        ! read two numbers + +    ! To write a file. +    open (unit=12, file="records.txt", status="replace") +    ! The file is referred to by a 'unit number', an integer that you pick in +    ! the range 9:99. Status can be one of {'old','replace','new'}. +    write (12, "(F10.2,F10.2,F10.2)") c, b, a +    close (12) + +    ! To read a file. +    open (newunit=m, file="records.txt", status="old") +    ! The file is referred to by a 'new unit number', an integer that the compiler +    ! picks for you. +    read (unit=m, fmt="(3F10.2)") a, b, c +    close (m) + +    ! There are more features available than discussed here and alternative +    ! variants due to backwards compatibility with older Fortran versions. + +    ! Built-in Functions +    ! ================== + +    ! Fortran has around 200 functions/subroutines intrinsic to the language. +    ! Examples - +    call cpu_time(v)        ! sets 'v' to a time in seconds. +    k = ior(i, j)           ! bitwise OR of 2 integers. +    v = log10(x)            ! log base 10. +    i = floor(b)            ! returns the closest integer less than or equal to x. +    v = aimag(w)            ! imaginary part of a complex number. + +    ! Functions & Subroutines +    ! ======================= + +    ! A subroutine runs some code on some input values and can cause +    ! side-effects or modify the input values. + +    call routine(a, c, v)   ! subroutine call. + +    ! A function takes a list of input parameters and returns a single value. +    ! However the input parameters may still be modified and side effects +    ! executed. + +    m = func(3, 2, k)       ! function call. + +    ! Function calls can also be evoked within expressions. +    print *, func2(3, 2, k) + +    ! A pure function is a function that doesn't modify its input parameters +    ! or cause any side-effects. +    m = func3(3, 2, k) + +contains                    ! Zone for defining sub-programs internal to the program. + +    ! Fortran has a couple of slightly different ways to define functions. + +    integer function func(a, b, c)      ! a function returning an integer value. +        ! implicit none                 ! subvariable fields can no longer declare implicit none +        integer, intent(in) :: a, b, c  ! type of input parameters defined inside the function. + +        if (a >= 2) then +            func = a + b + c            ! the return variable defaults to the function name. +            return                      ! can return the current value from the function at any time. +        end if +        func = a + c + +        ! Don't need a return statement at the end of a function. +    end function func + +    function func2(a, b, c) result(f)   ! return variable declared to be 'f'. +        integer, intent(in) :: a, b     ! can declare and enforce that variables +        !are not modified by the function. +        integer, intent(inout) :: c +        integer :: f                    ! function return type declared inside the function. +        integer :: cnt = 0              ! GOTCHA - initialisation implies variable is +        !saved between function calls. + +        f = a + b - c +        c = 4                           ! altering the value of an input variable. +        cnt = cnt + 1                   ! count number of function calls. + +    end function func2 + +    pure function func3(a, b, c)        ! a pure function can have no side-effects. +        integer, intent(in) :: a, b, c +        integer :: func3 + +        func3 = a*b*c + +    end function func3 + +    subroutine routine(d, e, f) +        real, intent(inout) :: f +        real, intent(in) :: d, e + +        f = 2*d + 3*e + f + +    end subroutine routine + +end program example                     ! End of Program Definition ----------------------- + +! Functions and Subroutines declared externally to the program listing need +! to be declared to the program using an Interface declaration (even if they +! are in the same source file!) (see below). It is easier to define them within +! the 'contains' section of a module or program. + +elemental real function func4(a) result(res) +! An elemental function is a Pure function that takes a scalar input variable +! but can also be used on an array where it will be separately applied to all +! of the elements of an array and return a new array. +    real, intent(in) :: a + +    res = a**2 + 1.0 + +end function func4 + +! Modules +! ======= + +! A module is a useful way to collect related declarations, functions and +! subroutines together for reusability. + +module fruit + +    real :: apple +    real :: pear +    real :: orange + +end module fruit + +module fruity +    ! Declarations must be in the order: modules, interfaces, variables. +    ! (can declare modules and interfaces in programs too). + +    use fruit, only: apple, pear    ! use apple and pear from fruit module. +    implicit none                   ! comes after module imports. + +    private                         ! make things private to the module (default is public). +    ! Declare some variables/functions explicitly public. +    public :: apple, mycar, create_mycar +    ! Declare some variables/functions private to the module (redundant here). +    private :: func4 + +    ! Interfaces +    ! ========== +    ! Explicitly declare an external function/procedure within the module +    ! (better in general to put functions/procedures in the 'contains' section). +    interface +        elemental real function func4(a) result(res) +            real, intent(in) :: a +        end function func4 +    end interface + +    ! Overloaded functions can be defined using named interfaces. +    interface myabs +        ! Can use 'module procedure' keyword to include functions already +        ! defined within the module. +        module procedure real_abs, complex_abs +    end interface + +    ! Derived Data Types +    ! ================== +    ! Can create custom structured data collections. +    type car +        character(len=100) :: model +        real :: weight              ! (kg) +        real :: dimensions(3)       ! i.e. length-width-height (metres). +        character :: colour +    contains +        procedure :: info           ! bind a procedure to a type. +    end type car + +    type(car) :: mycar              ! declare a variable of your custom type. +    ! See create_mycar() routine for usage. + +    ! Note: There are no executable statements in modules. + +contains + +    subroutine create_mycar(mycar) +        ! Demonstrates usage of a derived data type. +        type(car), intent(out) :: mycar + +        ! Access type elements using '%' operator. +        mycar%model = "Ford Prefect" +        mycar%colour = 'r' +        mycar%weight = 1400 +        mycar%dimensions(1) = 5.0   ! default indexing starts from 1! +        mycar%dimensions(2) = 3.0 +        mycar%dimensions(3) = 1.5 + +    end subroutine create_mycar + +    subroutine info(self) +        class(car), intent(in) :: self +        ! 'class' keyword used to bind a procedure to a type here. + +        print *, "Model     : ", self%model +        print *, "Colour    : ", self%colour +        print *, "Weight    : ", self%weight +        print *, "Dimensions: ", self%dimensions + +    end subroutine info + +    real pure function real_abs(x) +        real, intent(in) :: x + +        if (x < 0) then +            real_abs = -x +        else +            real_abs = x +        end if + +    end function real_abs + +    real pure function complex_abs(z) +        complex, intent(in) :: z +        ! long lines can be continued using the continuation character '&' + +        complex_abs = sqrt(real(z)**2 + & +                           aimag(z)**2) + +    end function complex_abs + +end module fruity + +``` + +### More Resources + +For more information on Fortran: + ++ [wikipedia](https://en.wikipedia.org/wiki/Fortran) ++ [Fortran-lang Organization](https://fortran-lang.org/) ++ [Fortran_95_language_features](https://en.wikipedia.org/wiki/Fortran_95_language_features) ++ [fortranwiki.org](http://fortranwiki.org) ++ [www.fortran90.org/](http://www.fortran90.org) ++ [list of Fortran 95 tutorials](http://www.dmoz.org/Computers/Programming/Languages/Fortran/FAQs%2C_Help%2C_and_Tutorials/Fortran_90_and_95/) ++ [Fortran wikibook](https://en.wikibooks.org/wiki/Fortran) ++ [Fortran resources](http://www.fortranplus.co.uk/resources/fortran_resources.pdf) ++ [Mistakes in Fortran 90 Programs That Might Surprise You](http://www.cs.rpi.edu/~szymansk/OOF90/bugs.html) | 
