diff --git a/.gitignore b/.gitignore index e8496bd94be7abe3aa4ae9342ceb7cbbe4b4d521..a4e96abba310cab0968a899e9057e68a6c13c77f 100644 --- a/.gitignore +++ b/.gitignore @@ -195,3 +195,4 @@ notebooks/*dataxy notebooks/*gnuxy src/*dataxy src/*gnuxy +fortran.html diff --git a/README.md b/README.md index b9158b8c64c29df310f96ee10dede898722c7a90..c36b2e9880ec67afc8c18c86b10c7eed5fdd78fd 100644 --- a/README.md +++ b/README.md @@ -2,1069 +2,6 @@ This is the repository for the training Learning Fortran -### Instructor +You can find the slides [here](./slides.pdf). -**Pierre-Yves Barriat** - -###### CISM/CÉCI Training Sessions - ---- - -# Fortran : shall we start ? - -- You know already one computer language ? -- You understand the very basic programming concepts : - - What is a variable, an assignment, function call, etc.? - - Why do I have to compile my code? - - What is an executable? -- You (may) already know some Fortran ? -- How to proceed from old Fortran, to much more modern languages like Fortran 90/2003 ? - ---- - -# Why to learn Fortran ? - -- Because of the execution `speed` of a program -- Well suited for numerical computations : -more than 45% of scientific applications are in Fortran -- `Fast` code : compilers can optimize well -- Optimized `numerical libraries` available -- Fortran is a `simple` langage and it is (kind-of) `easy to learn` - ---- - -# Fortran is simple - -- **We want to get our science done! Not learn languages!** -- How easy/difficult is it really to learn Fortran ? -- The concept is easy: -*variables, operators, controls, loops, subroutines/functions* -- **Invest some time now, gain big later!** - ---- - -# History - -**FOR**mula **TRAN**slation -> invented 1954-8 by John Backus and his team at IBM - -- FORTRAN 66 (ISO Standard 1972) -- FORTRAN 77 (1978) -- Fortran 90 (1991) -- Fortran 95 (1997) -- Fortran 2003 (2004) → `"standard" version` -- Fortran 2008 (2010) -- Fortran 2018 (11/2018) - ---- - -# Starting with Fortran 77 - -- Old Fortran provides only the absolute minimum! -- Basic features : -data containers (integer, float, ...), arrays, basic operators, loops, I/O, subroutines and functions -- But this version has flaws: -no dynamic memory allocation, old & obsolete constructs, “spaghetti†code, etc. -- Is that enough to write code ? - ---- - -# Fortran 77 → Fortran >90 - -- If Fortran 77 is so simple, why is it then so difficult to write good code? -- Is simple really better? -⇒ Using a language allows us to express our thoughts (on a computer) -- A more sophisticated language allows for more complex thoughts -- More language elements to get organized -⇒ Fortran 90/95/2003 (recursive, OOP, etc) - ---- - -# How to Build a FORTRAN Program - -FORTRAN is a compiled language (like C) so the source code (what you write) must be converted into machine code before it can be executed (e.g. Make command) - - - -> Fortran 77 source code [hello_world.f](https://gogs.elic.ucl.ac.be/pbarriat/learning-fortran/src/master/src/00_hello_world.f) - ---- - -# FORTRAN 77 Format - -This version requires a fixed format for programs - - - -- max length variable names is 6 characters -- alphanumeric only, must start with a letter -- character strings are case sensitive - ---- - -# FORTRAN >90 Format - -Versions >90 relaxe these requirements: - -- comments following statements (! delimiter) -- long variable names (31 characters) -- containing only letters, digits or underscore -- max row length is 132 characters -- can be max 39 continuation lines -- if a line is ended with ampersand (&), the line continues onto the next line -- semicolon (;) as a separator between statements on a single line -- allows free field input - ---- - -# Program Organization - -Most FORTRAN programs consist of a main program and one or more subprograms - -There is a fixed order: - -```Fortran90 -Heading -Declarations -Variable initializations -Program code -Format statements - -Subprogram definitions -(functions & subroutines) -``` - ---- - -# Data Type Declarations - -Basic data types are : - -- `INTEGER` : integer numbers (+/-) -- `REAL` : floating point numbers -- `DOUBLE PRECISION` : extended precision floating point -- `CHARACTER*n` : string with up to **n** characters -- `LOGICAL` : takes on values `.TRUE.` or `.FALSE.` - ---- - -# Data Type Declarations - -`INTEGER` and `REAL` can specify number of bytes to use - -- Default is: `INTEGER*4` and `REAL*4` -- `DOUBLE PRECISION` is same as `REAL*8` - -Arrays of any type must be declared: - -- `DIMENSION A(3,5)` - declares a 3 x 5 array -- `CHARACTER*30 NAME(50)` - directly declares a character array with 30 character strings in each element - ---- - -# Implicit vs Explicit Declarations - -By default, an implicit type is assumed depending on the first letter of the variable name: - -- `A-H, O-Z` define REAL variables -- `I-N` define INTEGER variables - -Can use the IMPLICIT statement: - -```fortran -IMPLICIT REAL (A-Z) -``` - -> makes all variables REAL if not declared - ---- - -# Implicit vs Explicit Declarations - -```fortran -IMPLICIT CHARACTER*2 (W) -``` - -> makes variables starting with W be 2-character strings - -```fortran -IMPLICIT DOUBLE PRECISION (D) -``` - -> makes variables starting with D be double precision - -**Good habit**: force explicit type declarations - -```fortran -IMPLICIT NONE -``` - -> user must explicitly declare all variable types - ---- - -# Assignment Statements - -**Old** assignment statement: `<label>` `<variable>` = `<expression>` - -- `<label>` : statement label number (1 to 99999) -- `<variable>` : FORTRAN variable -(max 6 characters, alphanumeric only for standard FORTRAN 77) - -**Expression**: - -- Numeric expressions: `VAR = 3.5*COS(THETA)` -- Character expressions: `DAY(1:3) = 'TUE'` -- Relational expressions: `FLAG = ANS .GT. 0` -- Logical expressions: `FLAG = F1 .OR. F2` - ---- - -# Numeric Expressions - -Arithmetic operators: precedence: `**` *(high)* → `-` *(low)* - -| Operator | Function | -| ------------ | --------------- | -| `**` | exponentiation | -| `*` | multiplication | -| `/` | division | -| `+` | addition | -| `-` | subtraction | - ---- - -# Numeric Expressions - -Numeric expressions are up-cast to the highest data type in the expression according to the precedence: - -*(low)* logical → integer → real → complex *(high)* - -and smaller byte size *(low)* to larger byte size *(high)* - -## Examples: - -> Fortran 77 source code [arith.f](https://gogs.elic.ucl.ac.be/pbarriat/learning-fortran/src/master/src/01_arith.f) -> Fortran 77 source code [sphere.f](https://gogs.elic.ucl.ac.be/pbarriat/learning-fortran/src/master/src/02_sphere.f) - ---- - -# Character Expressions - -Only built-in operator is **Concatenation** defined by `//` - -```fortran -'ILL'//'-'//'ADVISED' -``` - -`character` arrays are most commonly encountered - -- treated like any array (indexed using : notation) -- fixed length (usually padded with blanks) - ---- - -# Character Expressions - -Example: - -```fortran -CHARACTER FAMILY*16 -FAMILY = ‘GEORGE P. BURDELL’ - -PRINT*,FAMILY(:6) -PRINT*,FAMILY(8:9) -PRINT*,FAMILY(11:) -PRINT*,FAMILY(:6)//FAMILY(10:) -``` - -```fortran -GEORGE -P. -BURDELL -GEORGE BURDELL -``` - ---- - -# Relational Expressions - -Two expressions whose values are compared to determine whether the relation is true or false - -- may be numeric (common) or non-numeric - -`character` strings can be compared - -- done character by character -- shorter string is padded with blanks for comparison - ---- - -# Relational Expressions - -| Operator | Relationship | -| ------------ | --------------- | -| `.LT.` or `<` | less than | -| `.LE.` or `<=` | less than or equal to | -| `.EQ.` or `==` | equal to | -| `.NE.` or `/=` | not equal to | -| `.GT.` or `>` | greater than | -| `.GE.` or `>=` | greater than or equal to | - ---- - -# Logical Expressions - -Consists of one or more logical operators and logical, numeric or relational operands - -- values are `.TRUE.` or `.FALSE.` -- need to consider overall operator precedence - -> can combine logical and integer data with logical operators but this is tricky (**avoid!**) - ---- - -# Logical Expressions - -| F77 Operator | >F90 Operator | Example | Meaning | -| --------------- | --------------- | ------------ | --------------- | -| `.AND.` | `&&` | `A .AND. B` | logical `AND` | -| `.OR.` | `\|\|` | `A .OR. B` | logical `OR` | -| `.EQV.` | `==` | `A .EQV. B` | logical equivalence | -| `.NEQV.` | `/=` | `A .NEQV. B` | logical inequivalence | -| `.XOR.` | `/=` | `A .XOR. B` | exclusive `OR` (same as `.NEQV.`) | -| `.NOT.` | `!` | `.NOT. A` | logical negation | - ---- - -# Arrays in FORTRAN - -Arrays can be multi-dimensional (up to 7 in F77) and are indexed using `( )`: - -- `TEST(3)` or `FORCE(4,2)` - -> Indices are by default defined as `1...N` - -We can specify index range in declaration - -- `INTEGER K(0:11)` : `K` is dimensioned from `0-11` (12 elements) - -Arrays are stored in column order (1st column, 2nd column, etc) so accessing by incrementing row index first usually is fastest - -Whole array reference (only in >F90): `K(:)=-8` assigns 8 to all elements in K - -> Avoid `K=-8` assignement - ---- - -# Unconditional `GO TO` in F77 - -This is the only GOTO in FORTRAN 77 - -- Syntax: `GO TO label` -- Unconditional transfer to labeled statement - -```fortran - 10 -code- - GO TO 30 - -code that is bypassed- - 30 -code that is target of GOTO- - -more code- - GO TO 10 -``` - -- **Problem** : leads to confusing *"spaghetti code"* :boom: - ---- - -# `IF ELSE IF` Statement - -Basic version: - -```fortran -IF (KSTAT.EQ.1) THEN - CLASS='FRESHMAN' -ELSE IF (KSTAT.EQ.2) THEN - CLASS='SOPHOMORE' -ELSE IF (KSTAT.EQ.3) THEN - CLASS='JUNIOR' -ELSE IF (KSTAT.EQ.4) THEN - CLASS='SENIOR' -ELSE - CLASS='UNKNOWN' -ENDIF -``` - ---- - -# Spaghetti Code in F77 (and before) - -Use of `GO TO` and arithmetic `IF`'s leads to bad code that is very hard to maintain - -Here is the equivalent of an `IF-THEN-ELSE` statement: - -```fortran - 10 IF (KEY.LT.0) GO TO 20 - TEST=TEST-1 - THETA=ATAN(X,Y) - GO TO 30 - 20 TEST=TEST+1 - THETA=ATAN(-X,Y) - 30 CONTINUE -``` - -Now try to figure out what a complex `IF ELSE IF` statement would look like coded with this kind of simple `IF`... - ---- - -# Loop Statements (old versions) - -`DO` loop: structure that executes a specified number of times - -*Spaghetti Code Version* - -```fortran - K=2 - 10 PRINT*,A(K) - K=K+2 - IF (K.LE.11) GO TO 10 - 20 CONTINUE -``` - -*F77 Version* - -```fortran - DO 100 K=2,10,2 - PRINT*,A(K) - 100 CONTINUE -``` - ---- - -# Loop Statements (>F90) - -```fortran -DO K=2,10,2 - WRITE(*,*) A(K) -END DO -``` - -- Loop _control can include variables and a third parameter to specify increments, including negative values -- Loop always executes ONCE before testing for end condition - -```fortran -READ(*,*) R -DO WHILE (R.GE.0) - VOL=2*PI*R**2*CLEN - READ(*,*) R -END DO -``` - -- Loop will not execute at all if logical_expr is not true at start - ---- - -# Comments on Loop Statements - -In old versions: - -- to transfer out (exit loop), use a `GO TO` -- to skip to next loop, use `GO TO` terminating statement (this is a good reason to always make this a `CONTINUE` statement) - -In new versions: - -- to transfer out (exit loop), use `EXIT` statement and control is transferred to statement following loop end. This means you cannot transfer out of multiple nested loops with a single `EXIT` statement (use named loops if needed - `myloop : do i=1,n`). This is much like a `BREAK` statement in other languages. -- to skip to next loop cycle, use `CYCLE` statement in loop. - ---- - -# File-Directed Input and Output - -Much of early FORTRAN was devoted to reading input data -from Cards and writing to a line printer - -Today, most I/O is to and from a file: it requires more extensive I/O capabilities standardized until FORTRAN 77 - -**I/O** = communication between a program and the outside world - -- opening and closing a file with `OPEN` & `CLOSE` -- data reading & writing with `READ` & `WRITE` -- can use **unformatted** `READ` & `WRITE` if no human readable data are involved (much faster access, smaller files) - -> Fortran 77 source code [plot.f](https://gogs.elic.ucl.ac.be/pbarriat/learning-fortran/src/master/src/03_plot.f) - ---- - -# `OPEN` & `CLOSE` example - -Once opened, file is referred to by an assigned device number (a unique id) - -```fortran -character(len=*) :: x_name -integer :: ierr, iSize, guess_unit -logical :: itsopen, itexists -! -inquire(file=trim(x_name), size=iSize, number=guess_unit, opened=itsopen, exist=itexists) -if ( itsopen ) close(guess_unit, status='delete') -! -open(902,file=trim(x_name),status='new',iostat=ierr) -! -if (iSize <= 0 .OR. .NOT.itexists) then - open(902,file=trim(x_name),status='new',iostat=ierr) - if (ierr /= 0) then - ... - close(902) - endif - ... -endif -``` - ---- - -# `READ` Statement - -- syntax: `READ(dev_no, format_label) variable_list` -- read a record from `dev_no` using `format_label` and assign results to variables in `variable_list` - -```fortran - READ(105,1000) A,B,C - 1000 FORMAT(3F12.4) -``` - -> device numbers 1-7 are defined as standard I/O devices - -- each `READ` reads one or more lines of data and any remaining data in a line that is read is dropped if not translated to one of the variables in the `variable_list` -- `variable_list` can include implied `DO` such as: `READ(105,1000)(A(I),I=1,10)` - ---- - -# `READ` Statement - cont'd - -- input items can be integer, real or character -- characters must be enclosed in `' '` -- input items are separated by commas -- input items must agree in type with variables in `variable_list` -- each `READ` processes a new record (line) - -```fortran -INTEGER K -REAL(8) A,B -OPEN(105,FILE='path_to_existing_file') -READ(105,*) A,B,K -``` - -> read one line and look for floating point values for A and B and an integer for K - ---- - -# `WRITE` Statement - -- syntax: `WRITE(dev_no, format_label) variable_list` -- write variables in `variable_list` to output `dev_no` using format specified in format statement with `format_label` - -```fortran - WRITE(*,1000) A,B,KEY - 1000 FORMAT(F12.4,E14.5,I6) -``` - -```fortran -|----+----o----+----o----+----o----+----| - 1234.5678 -0.12345E+02 12 -``` - -- device number `*` is by default the screen (or *standard output* - also 6) -- each `WRITE` produces one or more output lines as needed to write out `variable_list` using `format` statement -- `variable_list` can include implied `DO` such as: `WRITE(*,2000)(A(I),I=1,10)` - -<!-- _footer: "" --> - ---- - -# `FORMAT` Statement - -| data type | format descriptors | example | -| --------------- | --------------- | ------------ | -| `integer` | `iw` | `write(*,'(i5)') int` | -| `real` (*decimal*) | `fw.d` | `write(*,'(f7.4)') x` | -| `real` (*exponential*) | `ew.d` | `write(*,'(e12.3)') y` | -| `character` | `a, aw` | `write(*,'(a)') string` | -| `logical` | `lw` | `write(*,'(l2)') test` | -| spaces & tabs | `wx` & `tw` | `write (*,'(i3,2x,f6.3)') i, x` | -| linebreak | `/` | `write (*,'(f6.3,/,f6.3)') x, y` | - ---- - -# `NAMELIST` - -It is possible to pre-define the structure of input and output data using `NAMELIST` in order to make it easier to process with `READ` and `WRITE` statements - -- Use `NAMELIST` to define the data structure -- Use `READ` or `WRITE` with reference to `NAMELIST` to handle the data in the specified format - -> This is not part of standard F77 but it is included in >F90 - ---- - -# `NAMELIST` - cont'd - -On input, the `NAMELIST` data must be structured as follows: - -```fortran -&INPUT - THICK=0.245, - LENGTH=12.34, - WIDTH=2.34, - DENSITY=0.0034 -/ -``` - -> Fortran 90 source code [namelist.f90](https://gogs.elic.ucl.ac.be/pbarriat/learning-fortran/src/master/src/04_namelist.f90) -> Namelist file [namelist.def](https://gogs.elic.ucl.ac.be/pbarriat/learning-fortran/src/master/src/04_namelist.def) - ---- - -# Internal `WRITE` Statement - -Internal `WRITE` does same as `ENCODE` in F77 : **a cast to string** -> `WRITE (dev_no, format_label) var_list` -> write variables in `var_list` to internal storage defined by character variable used as `dev_no` = default character variable (not an array) - -```fortran -INTEGER*4 J,K -CHARACTER*50 CHAR50 -DATA J,K/1,2/ -... -WRITE(CHAR50,*) J,K -``` - -Results: - -```fortran -CHAR50=' 1 2' -``` - ---- - -# Internal `READ` Statement - -Internal `READ` does same as `DECODE` in F77 : **a cast from string** -> `READ (dev_no, format_label) var_list` -> read variables from internal storage specified by character variable used as `dev_no` = default character variable (not an array) - -```fortran -INTEGER K -REAL A,B -CHARACTER*80 REC80 -DATA REC80/'1.2, 2.3, -5'/ -... -READ(REC80,*) A,B,K -``` - -Results: - -```fortran -A=1.2, B=2.3, K=-5 -``` - -<!-- _footer: "" --> - ---- - -# Structured programming - -Structured programming is based on subprograms (functions and subroutines) and control statements (like `IF` statements or loops) : - -- structure the control-flow of your programs (eg, give up the `GO TO`) -- improved readability -- lower level aspect of coding in a smart way - -It is a **programming paradigm** aimed at improving the quality, clarity, and access time of a computer program - ---- - -# Functions and Subroutines - -`FUNCTION` & `SUBROUTINE` are subprograms that allow structured coding - -- `FUNCTION`: returns a single explicit function value for given function arguments - It’s also a variable → so must be declared ! -- `SUBROUTINE`: any values returned must be returned through the arguments (no explicit subroutine value is returned) -- functions and subroutines are **not recursive in F77** - -Subprograms use a separate namespace for each subprogram so that variables are local to the subprogram - -- variables are passed to subprogram through argument list and returned in function value or through arguments -- variables stored in `COMMON` may be shared between namespaces - -<!-- _footer: "" --> - ---- - -# Functions and Subroutines - cont'd - -Subprograms must include at least one `RETURN` (can have more) and be terminated by an `END` statement - -`FUNCTION` example: - -```fortran -REAL FUNCTION AVG3(A,B,C) -AVG3=(A+B+C)/3 -RETURN -END -``` - -Use: - -```fortran -AV = WEIGHT*AVG3(A1,F2,B2) -``` - -> `FUNCTION` type is implicitly defined as REAL - ---- - -# Functions and Subroutines - cont'd - -Subroutine is invoked using the `CALL` statement - -```fortran -SUBROUTINE AVG3S(A,B,C,AVERAGE) -AVERAGE=(A+B+C)/3 -RETURN -END -``` - -Use: - -```fortran -CALL AVG3S(A1,F2,B2,AVR) -RESULT = WEIGHT*AVR -``` - -Any returned values must be returned through argument list - -> Fortran 90 source code [newton.f90](https://gogs.elic.ucl.ac.be/pbarriat/learning-fortran/src/master/src/05_newton.f90) - ---- - -# Arguments - -Arguments in subprogram are `dummy` arguments used in place of the real arguments - -- arguments are passed by **reference** (memory address) if given as *symbolic* - the subprogram can then alter the actual argument value since it can access it by reference -- arguments are passed by **value** if given as *literal* (so cannot be modified) - -```fortran -CALL AVG3S(A1,3.4,C1,QAV) -``` - -> 2nd argument is passed by value - QAV contains result - -```fortran -CALL AVG3S(A,C,B,4.1) -``` - -> no return value is available since "4.1" is a value and not a reference to a variable! - ---- - -# Arguments - cont'd - -- `dummy` arguments appearing in a subprogram declaration cannot be an individual array element reference, e.g., `A(2)`, or a *literal*, for obvious reasons! -- arguments used in invocation (by calling program) may be *variables*, *subscripted variables*, *array names*, *literals*, *expressions* or *function names* -- using symbolic arguments (variables or array names) is the **only way** to return a value (result) from a `SUBROUTINE` - -> It is considered **BAD coding practice**, but functions can return values by changing the value of arguments - This type of use should be strictly **avoided**! - ---- - -# Arguments - cont'd - -The `INTENT` keyword (>F90) increases readability and enables better compile-time error checking - -```fortran -SUBROUTINE AVG3S(A,B,C,AVERAGE) - IMPLICIT NONE - REAL, INTENT(IN) :: A, B - REAL, INTENT(INOUT) :: C ! default - REAL, INTENT(OUT) :: AVERAGE - - A = 10 ! Compilation error - C = 10 ! Correct - AVERAGE=(A+B+C)/3 ! Correct -END -``` - -> Compiler uses `INTENT` for error checking and optimization - ---- - -# `FUNCTION` versus Array - -`REMAINDER(4,3)` could be a 2D array or it could be a reference to a function - -If the name, including arguments, **matches an array declaration**, then it is taken to be an array, **otherwise**, it is assumed to be a `FUNCTION` - -Be careful about `implicit` versus `explicit` type declarations with `FUNCTION` - -```fortran -PROGRAM MAIN - INTEGER REMAINDER - ... - KR = REMAINDER(4,3) - ... -END - -INTEGER FUNCTION REMAINDER(INUM,IDEN) - ... -END -``` - -<!-- _footer: "" --> - ---- - -# Arrays with Subprograms - -Arrays present special problems in subprograms - -- must pass by reference to subprogram since there is no way to list array values explicitly as literals -- how do you tell subprogram how large the array is ? - -> Answer varies with FORTRAN version and vendor (dialect)... - -When an array element, e.g. `A(1)`, is used in a subprogram invocation (in calling program), it is passed as a reference (address), just like a simple variable - -When an array is used by name in a subprogram invocation (in calling program), it is passed as a reference to the entire array. In this case the array must be appropriately dimensioned in the subroutine (and this can be tricky...) - ---- - -# Arrays - cont'd - -### Data layout in multi-dimensional arrays - -- always increment the left-most index of multi-dimensional arrays in the innermost loop (i.e. fastest) -- **column major** ordering in Fortran vs. **row major** ordering in C -- a compiler (with sufficient optimization flags) may re-order loops automatically - -```fortran -do j=1,M - do i=1,N ! innermost loop - y(i) = y(i)+ a(i,j)*x(j) ! left-most index is i - end do -end do -``` - ---- - -# Arrays - cont'd - -- dynamically allocate memory for arrays using `ALLOCATABLE` on declaration -- memory is allocated through `ALLOCATE` statement in the code and is deallocated through `DEALLOCATE` statement - -```fortran -integer :: m, n -integer, allocatable :: idx(:) -real, allocatable :: mat(:,:) -m = 100 ; n = 200 -allocate( idx(0:m-1)) -allocate( mat(m, n)) -... -deallocate(idx , mat) -``` - -> It exists many array intrinsic functions: SIZE, SHAPE, SUM, ANY, MINVAL, MAXLOC, RESHAPE, DOT_PRODUCT, TRANSPOSE, WHERE, FORALL, etc - ---- - -# `COMMON` & `MODULE` Statement - -The `COMMON` statement allows variables to have a more extensive scope than otherwise - -- a variable declared in a `Main Program` can be made accessible to subprograms (without appearing in argument lists of a calling statement) -- this can be selective (don't have to share all everywhere) -- **placement**: among type declarations, after `IMPLICIT` or `EXPLICIT`, before `DATA` statements -- can group into **labeled** `COMMON` - -With > F90, it's better to use the `MODULE` subprogram instead of the `COMMON` statement - -> Fortran 77 source code [common.f](https://gogs.elic.ucl.ac.be/pbarriat/learning-fortran/src/master/src/06_common.f) - Fortran 90 source code [module.f90](https://gogs.elic.ucl.ac.be/pbarriat/learning-fortran/src/master/src/06_module.f90) - ---- - -# Modular programming (>F90) - -Modular programming is about separating parts of programs into independent and interchangeable modules : - -- improve testability -- improve maintainability -- re-use of code -- higher level aspect of coding in a smart way -- *separation of concerns* - -The principle is that making significant parts of the code independent, replaceable and independently testable makes your programs **more maintainable** - ---- - -# Data Type Declarations - -FORTRAN >90 allows user derived types - -```fortran -TYPE my_variable - character(30) :: name - integer :: id - real(8) :: value - integer, dimension(3,3) :: dimIndex -END TYPE variable - -type(my_variable) var -var%name = "salinity" -var%id = 1 -``` - ---- - -# Subprograms type - -`MODULE` are subprograms that allow modular coding and data encapsulation - -The interface of a subprogram type is **explicit** or **implicit** - -Several types of subprograms: - -- `intrinsic`: explicit - defined by Fortran itself (trignonometric functions, etc) -- `module`: explicit - defined with `MODULE` statement and used with `USE` -- `internal`: explicit - defined with `CONTAINS` statement inside (sub)programs -- `external`: implicit (but can be manually (re)defined explicit) - e.g. **libraries** - -Differ with the **scope**: what data and other subprograms a subprogram can access - ---- - -# `MODULE` type - -```fortran -MODULE example - IMPLICIT NONE - INTEGER, PARAMETER :: index = 10 - REAL(8), SAVE :: latitude -CONTAINS - FUNCTION check(x) RESULT(z) - INTEGER :: x, z - ... - END FUNCTION check -END MODULE example -``` - -```fortran -PROGRAM myprog - USE example, ONLY: check, latitude - IMPLICIT NONE - ... - test = check(a) - ... -END PROGRAM myprog -``` - -<!-- _footer: "" --> - ---- - -# `internal` subprogams - -```fortran -program main - implicit none - integer N - real X(20) - ... - write(*,*), 'Processing x...', process() - ... -contains - logical function process() - ! in this function N and X can be accessed directly (scope of main) - ! Please not that this method is not recommended: - ! it would be better to pass X as an argument of process - implicit none - if (sum(x) > 5.) then - process = .FALSE. - else - process = .TRUE. - endif - end function process -end program -``` - -<!-- _footer: "" --> - ---- - -# `external` subprogams - -- `external` subprogams are defined in a separate program unit -- to use them in another program unit, refer with the `EXTERNAL` statement -- compiled separately and linked - -**!!! DO NOT USE THEM**: modules are much easier and more robust :exclamation: - -They are only needed when subprogams are written with different programming language or when using external libraries (such as BLAS) - -> It's **highly** recommended to construct `INTERFACE` blocks for any external subprogams used - ---- - -# `interface` statement - -```fortran -SUBROUTINE nag_rand(table) - INTERFACE - SUBROUTINE g05faf(a,b,n,x) - REAL, INTENT(IN) :: a, b - INTEGER, INTENT(IN) :: n - REAL, INTENT(OUT) :: x(n) - END SUBROUTINE g05faf - END INTERFACE - ! - REAL, DIMENSION(:), INTENT(OUT) :: table - ! - call g05faf(-1.0,-1.0, SIZE(table), table) -END SUBROUTINE nag_rand -``` - -<!-- _footer: "" --> - ---- - -# Fortran Compiler and libraries - -Examples: - -```bash -module load netCDF-Fortran/4.5.3-gompi-2021b -gfortran -ffree-line-length-none \ --o OceanGrideChange.exe 07_OceanGrideChange.f90 \ --I${EBROOTNETCDFMINFORTRAN}/include -L${EBROOTNETCDFMINFORTRAN}/lib -lnetcdff -``` - -```bash -module load netCDF-Fortran/4.5.3-iimpi-2021b -ifort -O3 \ --o OceanGrideChange.exe 07_OceanGrideChange.f90 \ --I${EBROOTNETCDFMINFORTRAN}/include -L${EBROOTNETCDFMINFORTRAN}/lib -lnetcdff -``` - -> Fortran 90 source code [OceanGrideChange.f90](https://gogs.elic.ucl.ac.be/pbarriat/learning-fortran/src/master/src/07_OceanGrideChange.f90) with the input file [input.nc](https://gogs.elic.ucl.ac.be/pbarriat/learning-fortran/src/master/src/07_input.nc) - ---- - -# Conclusions - -- Fortran in all its standard versions and vendor-specific dialects is a rich but confusing language -- Fortran is a modern language that continues to evolve - -- Fortran is still ideally suited for numerical computations in engineering and science - - most new language features have been added since F95 - - "High Performance Fortran" includes capabilities designed for parallel processing +Presentation is [here](https://www.elic.ucl.ac.be/users/pbarriat/slides/fortran.html) diff --git a/assets/FortranCISM.pdf b/assets/FortranCISM.pdf deleted file mode 100644 index 7ff8da6fbd08e8ea4a88b7dd983cf9facada21db..0000000000000000000000000000000000000000 Binary files a/assets/FortranCISM.pdf and /dev/null differ diff --git a/assets/back.png b/assets/back.png new file mode 100644 index 0000000000000000000000000000000000000000..0674657248db1242420acd99ec7d510b8f3236cd Binary files /dev/null and b/assets/back.png differ diff --git a/assets/garde.png b/assets/garde.png new file mode 100644 index 0000000000000000000000000000000000000000..7c82359c3ac6126ffeed79a081ab2c75c00e19dc Binary files /dev/null and b/assets/garde.png differ diff --git a/assets/marp.config.js b/assets/marp.config.js new file mode 100644 index 0000000000000000000000000000000000000000..c5a35bca5b0623dc640ce779365a6d00bf5dab64 --- /dev/null +++ b/assets/marp.config.js @@ -0,0 +1,5 @@ +const marpKrokiPlugin = require('./kroki-plugin') + +module.exports = { + engine: ({ marp }) => marp.use(marpKrokiPlugin) +} diff --git a/assets/tum.css b/assets/tum.css new file mode 100644 index 0000000000000000000000000000000000000000..08d289362d7399b163db43fbc0e37f9b4a6c9397 --- /dev/null +++ b/assets/tum.css @@ -0,0 +1,55 @@ +/* @theme tum */ + +@import 'default'; + +section { + /*background-color: #fff; + color: #000; + background-image: url('images/TUM_Logo_blau_rgb_s.svg'); + background-repeat: no-repeat; + background-position: right 40px top 40px; + background-size: 8%;*/ +} + +section.lead { + /*background-image: url('images/TUM_Uhrenturm.png'); + background-position: right; + background-size: 45%;*/ +} + +section h1, +section h2 { + color: #1f315c; +} +section a { + color: #5fb2e6; +} +section footer, +section::after { + color: #9cb7d4; +} + +section.invert { + background-color: #003359; + color: #fff; + /*background-image: url('images/TUM_Logo_weiss_rgb_s.svg');*/ +} + +section.lead.invert { + /*background-image: url('images/TUM_Uhrenturm_w.png');*/ +} + +section.invert h1, +section.invert footer, +section.invert::after { + color: #fff; +} + +section.invert a { + color: #e37222; +} + +/* Add "Page" prefix and total page number */ +section::after { + content: attr(data-marpit-pagination) ' / ' attr(data-marpit-pagination-total); +} diff --git a/compile.sh b/compile.sh new file mode 100755 index 0000000000000000000000000000000000000000..fbc596eef4f83460935e279ea4937c9efbc12669 --- /dev/null +++ b/compile.sh @@ -0,0 +1,10 @@ +#!/bin/bash +# +# PY Barriat, October 2022 +# +# Download and install marp (MarkDown slides extension) from here: +# https://github.com/marp-team/marp-cli/releases +# + +marp --allow-local-files --theme ./assets/tum.css slides.md -o slides.pdf +marp --template bespoke --bespoke.progress --allow-local-files --theme ./assets/tum.css slides.md -o fortran.html diff --git a/slides.md b/slides.md new file mode 100644 index 0000000000000000000000000000000000000000..258fb855a4ea238feb3b6ba911152e79459a76d9 --- /dev/null +++ b/slides.md @@ -0,0 +1,1138 @@ +--- +marp: true +title: Introduction to structured programming with Fortran +author: P.Y. Barriat +description: https://dev.to/nikolab/complete-list-of-github-markdown-emoji-markup-5aia +backgroundImage: url('assets/back.png') +_backgroundImage: url('assets/garde.png') +footer: 09/11/2023 | Introduction to structured programming with Fortran +_footer: "" +paginate: true +_paginate: false +--- + +Introduction to structured programming with `Fortran`<!--fit--> +=== + +https://gogs.elic.ucl.ac.be/pbarriat/learning-fortran + + + +### Pierre-Yves Barriat + +##### November 09, 2023 + +###### CISM/CÉCI Training Sessions + +--- + +# Fortran : shall we start ? + +- You know already one computer language ? +- You understand the very basic programming concepts : + - What is a variable, an assignment, function call, etc.? + - Why do I have to compile my code? + - What is an executable? +- You (may) already know some Fortran ? +- How to proceed from old Fortran, to much more modern languages like Fortran 90/2003 ? + +--- + +# Why to learn Fortran ? + +- Because of the execution `speed` of a program +- Well suited for numerical computations : +more than 45% of scientific applications are in Fortran +- `Fast` code : compilers can optimize well +- Optimized `numerical libraries` available +- Fortran is a `simple` langage and it is (kind-of) `easy to learn` + +--- + +# Fortran is simple + +- **We want to get our science done! Not learn languages!** +- How easy/difficult is it really to learn Fortran ? +- The concept is easy: +*variables, operators, controls, loops, subroutines/functions* +- **Invest some time now, gain big later!** + +--- + +# History + +**FOR**mula **TRAN**slation +> invented 1954-8 by John Backus and his team at IBM + +- FORTRAN 66 (ISO Standard 1972) +- FORTRAN 77 (1978) +- Fortran 90 (1991) +- Fortran 95 (1997) +- Fortran 2003 (2004) → `"standard" version` +- Fortran 2008 (2010) +- Fortran 2018 (11/2018) + +--- + +# Starting with Fortran 77 + +- Old Fortran provides only the absolute minimum! +- Basic features : +data containers (integer, float, ...), arrays, basic operators, loops, I/O, subroutines and functions +- But this version has flaws: +no dynamic memory allocation, old & obsolete constructs, “spaghetti†code, etc. +- Is that enough to write code ? + +--- + +# Fortran 77 → Fortran >90 + +- If Fortran 77 is so simple, why is it then so difficult to write good code? +- Is simple really better? +⇒ Using a language allows us to express our thoughts (on a computer) +- A more sophisticated language allows for more complex thoughts +- More language elements to get organized +⇒ Fortran 90/95/2003 (recursive, OOP, etc) + +--- + +# How to Build a FORTRAN Program + +FORTRAN is a compiled language (like C) so the source code (what you write) must be converted into machine code before it can be executed (e.g. Make command) + + + +> Fortran 77 source code [hello_world.f](https://gogs.elic.ucl.ac.be/pbarriat/learning-fortran/src/master/src/00_hello_world.f) + +--- + +# FORTRAN 77 Format + +This version requires a fixed format for programs + + + +- max length variable names is 6 characters +- alphanumeric only, must start with a letter +- character strings are case sensitive + +--- + +# FORTRAN >90 Format + +Versions >90 relaxe these requirements: + +- comments following statements (! delimiter) +- long variable names (31 characters) +- containing only letters, digits or underscore +- max row length is 132 characters +- can be max 39 continuation lines +- if a line is ended with ampersand (&), the line continues onto the next line +- semicolon (;) as a separator between statements on a single line + +--- + +# Program Organization + +Most FORTRAN programs consist of a main program and one or more subprograms + +There is a fixed order: + +```Fortran90 +Heading +Declarations +Variable initializations +Program code +Format statements + +Subprogram definitions +(functions & subroutines) +``` + +--- + +# Data Type Declarations + +Basic data types are : + +- `INTEGER` : integer numbers (+/-) +- `REAL` : floating point numbers +- `DOUBLE PRECISION` : extended precision floating point +- `CHARACTER*n` : string with up to **n** characters +- `LOGICAL` : takes on values `.TRUE.` or `.FALSE.` + +--- + +# Data Type Declarations + +`INTEGER` and `REAL` can specify number of bytes to use + +- Default is: `INTEGER*4` and `REAL*4` +- `DOUBLE PRECISION` is same as `REAL*8` + +Arrays of any type must be declared: + +- `DIMENSION A(3,5)` - declares a 3 x 5 array +- `CHARACTER*30 NAME(50)` - directly declares a `character` array with 30 `character` strings in each element + +--- + +# Implicit vs Explicit Declarations + +By default, an implicit type is assumed depending on the first letter of the variable name: + +- `A-H, O-Z` define REAL variables +- `I-N` define INTEGER variables + +Can use the IMPLICIT statement: + +```fortran +IMPLICIT REAL (A-Z) +``` + +> makes all variables REAL if not declared + +--- + +# Implicit vs Explicit Declarations + +```fortran +IMPLICIT CHARACTER*2 (W) +``` + +> makes variables starting with W be 2-character strings + +```fortran +IMPLICIT DOUBLE PRECISION (D) +``` + +> makes variables starting with D be double precision + +**Good habit**: force explicit type declarations + +```fortran +IMPLICIT NONE +``` + +> user must explicitly declare all variable types + +--- + +# Assignment Statements + +**Old** assignment statement: `<label>` `<variable>` = `<expression>` + +- `<label>` : statement label number (1 to 99999) +- `<variable>` : FORTRAN variable +(max 6 characters, alphanumeric only for standard FORTRAN 77) + +**Expression**: + +- Numeric expressions: `VAR = 3.5*COS(THETA)` +- Character expressions: `DAY(1:3) = 'TUE'` +- Relational expressions: `FLAG = ANS .GT. 0` +- Logical expressions: `FLAG = F1 .OR. F2` + +--- + +# Numeric Expressions + +Arithmetic operators: precedence: `**` *(high)* → `-` *(low)* + +| Operator | Function | +| ------------ | --------------- | +| `**` | exponentiation | +| `*` | multiplication | +| `/` | division | +| `+` | addition | +| `-` | subtraction | + +--- + +# Numeric Expressions + +Numeric expressions are up-cast to the highest data type in the expression according to the precedence: + +*(low)* logical → integer → real → complex *(high)* + +and smaller byte size *(low)* to larger byte size *(high)* + +## Examples: + +> Fortran 77 source code [arith.f](https://gogs.elic.ucl.ac.be/pbarriat/learning-fortran/src/master/src/01_arith.f) +> Fortran 77 source code [sphere.f](https://gogs.elic.ucl.ac.be/pbarriat/learning-fortran/src/master/src/02_sphere.f) + +--- + +# Character Expressions + +Only built-in operator is **Concatenation** defined by `//` + +```fortran +'ILL'//'-'//'ADVISED' +``` + +`character` arrays are most commonly encountered + +- treated like any array (indexed using : notation) +- fixed length (usually padded with blanks) + +--- + +# Character Expressions + +Example: + +```fortran +CHARACTER FAMILY*16 +FAMILY = ‘GEORGE P. BURDELL’ + +PRINT*,FAMILY(:6) +PRINT*,FAMILY(8:9) +PRINT*,FAMILY(11:) +PRINT*,FAMILY(:6)//FAMILY(10:) +``` + +```fortran +GEORGE +P. +BURDELL +GEORGE BURDELL +``` + +--- + +# Relational Expressions + +Two expressions whose values are compared to determine whether the relation is true or false + +- may be numeric (common) or non-numeric + +`character` strings can be compared + +- done character by character +- shorter string is padded with blanks for comparison + +--- + +# Relational Expressions + +| Operator | Relationship | +| ------------ | --------------- | +| `.LT.` or `<` | less than | +| `.LE.` or `<=` | less than or equal to | +| `.EQ.` or `==` | equal to | +| `.NE.` or `/=` | not equal to | +| `.GT.` or `>` | greater than | +| `.GE.` or `>=` | greater than or equal to | + +--- + +# Logical Expressions + +Consists of one or more logical operators and logical, numeric or relational operands + +- values are `.TRUE.` or `.FALSE.` +- need to consider overall operator precedence + +> can combine logical and integer data with logical operators but this is tricky (**avoid!**) + +--- + +# Logical Expressions + +| F77 Operator | >F90 Operator | Example | Meaning | +| --------------- | --------------- | ------------ | --------------- | +| `.AND.` | `&&` | `A .AND. B` | logical `AND` | +| `.OR.` | `\|\|` | `A .OR. B` | logical `OR` | +| `.EQV.` | `==` | `A .EQV. B` | logical equivalence | +| `.NEQV.` | `/=` | `A .NEQV. B` | logical inequivalence | +| `.XOR.` | `/=` | `A .XOR. B` | exclusive `OR` (same as `.NEQV.`) | +| `.NOT.` | `!` | `.NOT. A` | logical negation | + +--- + +# Arrays in FORTRAN + +Arrays can be multi-dimensional (up to 7 in F77) and are indexed using `( )`: + +- `TEST(3)` or `FORCE(4,2)` + +> Indices are by default defined as `1...N` + +We can specify index range in declaration + +- `INTEGER K(0:11)` : `K` is dimensioned from `0-11` (12 elements) + +Arrays are stored in column order (1st column, 2nd column, etc) so accessing by **incrementing row index first** usually is **fastest** (see later) + +Whole array reference (only in >F90): `K(:)=-8` assigns 8 to all elements in K + +> Avoid `K=-8` assignement + +--- + +# Unconditional `GO TO` in F77 + +This is the only GOTO in FORTRAN 77 + +- Syntax: `GO TO label` +- Unconditional transfer to labeled statement + +```fortran + 10 -code- + GO TO 30 + -code that is bypassed- + 30 -code that is target of GOTO- + -more code- + GO TO 10 +``` + +- **Problem** : leads to confusing *"spaghetti code"* :boom: + +--- + +# `IF ELSE IF` Statement + +Basic version: + +```fortran +IF (KSTAT.EQ.1) THEN + CLASS='FRESHMAN' +ELSE IF (KSTAT.EQ.2) THEN + CLASS='SOPHOMORE' +ELSE IF (KSTAT.EQ.3) THEN + CLASS='JUNIOR' +ELSE IF (KSTAT.EQ.4) THEN + CLASS='SENIOR' +ELSE + CLASS='UNKNOWN' +ENDIF +``` + +--- + +# Spaghetti Code in F77 (and before) + +Use of `GO TO` and arithmetic `IF`'s leads to bad code that is very hard to maintain + +Here is the equivalent of an `IF-THEN-ELSE` statement: + +```fortran + 10 IF (KEY.LT.0) GO TO 20 + TEST=TEST-1 + THETA=ATAN(X,Y) + GO TO 30 + 20 TEST=TEST+1 + THETA=ATAN(-X,Y) + 30 CONTINUE +``` + +Now try to figure out what a complex `IF ELSE IF` statement would look like coded with this kind of simple `IF`... + +--- + +# Loop Statements (old versions) + +`DO` loop: structure that executes a specified number of times + +*Spaghetti Code Version* + +```fortran + K=2 + 10 PRINT*,A(K) + K=K+2 + IF (K.LE.11) GO TO 10 + 20 CONTINUE +``` + +*F77 Version* + +```fortran + DO 100 K=2,10,2 + PRINT*,A(K) + 100 CONTINUE +``` + +--- + +# Loop Statements (>F90) + +```fortran +DO K=2,10,2 + WRITE(*,*) A(K) +END DO +``` + +- `loop_control` can include variables and a third parameter to specify increments, including negative values +- loop always executes ONCE before testing for end condition + +```fortran +READ(*,*) R +DO WHILE (R.GE.0) + VOL=2*PI*R**2*CLEN + READ(*,*) R +END DO +``` + +- Loop will not execute at all if `logical_expr` is not true at start + +--- + +# Comments on Loop Statements + +In old versions: + +- to transfer out (exit loop), use a `GO TO` +- to skip to next loop, use `GO TO` terminating statement (this is a good reason to always make this a `CONTINUE` statement) + +In new versions: + +- to transfer out (exit loop), use `EXIT` statement and control is transferred to statement following loop end. This means you cannot transfer out of multiple nested loops with a single `EXIT` statement (use named loops if needed - `myloop : do i=1,n`). This is much like a `BREAK` statement in other languages. +- to skip to next loop cycle, use `CYCLE` statement in loop. + +--- + +# File-Directed Input and Output + +Much of early FORTRAN was devoted to reading input data +from "cards" and writing to a line printer + +Today, most I/O is to and from a file: it requires more extensive I/O capabilities standardized until FORTRAN 77 + +**I/O** = communication between a program and the outside world + +- opening and closing a file with `OPEN` & `CLOSE` +- data reading & writing with `READ` & `WRITE` +- can use **unformatted** `READ` & `WRITE` if no human readable data are involved (much faster access, smaller files) + +> Fortran 77 source code [plot.f](https://gogs.elic.ucl.ac.be/pbarriat/learning-fortran/src/master/src/03_plot.f) + +--- + +# `READ` Statement + +- syntax: `READ(dev_no, format_label) variable_list` +- read a record from `dev_no` using `format_label` and assign results to variables in `variable_list` + +```fortran + READ(105,1000) A,B,C + 1000 FORMAT(3F12.4) +``` + +> device numbers 1-7 are defined as standard I/O devices + +- each `READ` reads one or more lines of data and any remaining data in a line that is read is dropped if not translated to one of the variables in the `variable_list` +- `variable_list` can include implied `DO` such as: `READ(105,1000)(A(I),I=1,10)` + +--- + +# `READ` Statement - cont'd + +- input items can be integer, real or character +- characters must be enclosed in `' '` (or `" "`) +- input items are separated by commas +- input items must agree in type with variables in `variable_list` +- each `READ` processes a new record (line) + +```fortran +INTEGER K +REAL(8) A,B +OPEN(105,FILE='path_to_existing_file') +READ(105,*) A,B,K +``` + +> read one line and look for floating point values for A and B and an integer for K + +--- + +# `WRITE` Statement + +- syntax: `WRITE(dev_no, format_label) variable_list` +- write variables in `variable_list` to output `dev_no` using format specified in format statement with `format_label` + +```fortran + WRITE(*,1000) A,B,KEY + 1000 FORMAT(F12.4,E14.5,I6) +``` + +```fortran +|----+----o----+----o----+----o----+----| + 1234.5678 -0.12345E+02 12 +``` + +- device number `*` is by default the screen (or *standard output* - also 6) +- each `WRITE` produces one or more output lines as needed to write out `variable_list` using `format` statement +- `variable_list` can include implied `DO` such as: `WRITE(*,2000)(A(I),I=1,10)` + +<!-- _footer: "" --> + +--- + +# `FORMAT` Statement + +| data type | format descriptors | example | +| --------------- | --------------- | ------------ | +| `integer` | `iw` | `write(*,'(i5)') int` | +| `real` (*decimal*) | `fw.d` | `write(*,'(f7.4)') x` | +| `real` (*exponential*) | `ew.d` | `write(*,'(e12.3)') y` | +| `character` | `a, aw` | `write(*,'(a)') string` | +| `logical` | `lw` | `write(*,'(l2)') test` | +| spaces & tabs | `wx` & `tw` | `write (*,'(i3,2x,f6.3)') i, x` | +| linebreak | `/` | `write (*,'(f6.3,/,f6.3)') x, y` | + +--- + +# `OPEN` & `CLOSE` example (>F90) + +Once opened, file is referred to by an assigned device number (a unique id) + +```fortran +character(len=*) :: x_name +integer :: ierr, iSize, guess_unit +logical :: itsopen, itexists +! +inquire(file=trim(x_name), size=iSize, number=guess_unit, opened=itsopen, exist=itexists) +if ( itsopen ) close(guess_unit, status='delete') +! +open(902,file=trim(x_name),status='new',iostat=ierr) +! +if (iSize <= 0 .OR. .NOT.itexists) then + open(902,file=trim(x_name),status='new',iostat=ierr) + if (ierr /= 0) then + ... + close(902) + endif + ... +endif +``` + +--- + +# `NAMELIST` + +It is possible to pre-define the structure of input and output data using `NAMELIST` in order to make it easier to process with `READ` and `WRITE` statements + +- Use `NAMELIST` to define the data structure +- Use `READ` or `WRITE` with reference to `NAMELIST` to handle the data in the specified format + +> This is not part of standard F77 but it is included in >F90 + +--- + +# `NAMELIST` - cont'd + +On input, the `NAMELIST` data must be structured as follows: + +```fortran +&INPUT + THICK=0.245, + LENGTH=12.34, + WIDTH=2.34, + DENSITY=0.0034 +/ +``` + +> Fortran 90 source code [namelist.f90](https://gogs.elic.ucl.ac.be/pbarriat/learning-fortran/src/master/src/04_namelist.f90) +> Namelist file [namelist.def](https://gogs.elic.ucl.ac.be/pbarriat/learning-fortran/src/master/src/04_namelist.def) + +--- + +# Internal `WRITE` Statement + +Internal `WRITE` does same as `ENCODE` in F77 : **a cast to string** +> `WRITE (dev_no, format_label) var_list` +> write variables in `var_list` to internal storage defined by character variable used as `dev_no` = default character variable (not an array) + +```fortran +INTEGER*4 J,K +CHARACTER*50 CHAR50 +DATA J,K/1,2/ +... +WRITE(CHAR50,*) J,K +``` + +Results: + +```fortran +CHAR50=' 1 2' +``` + +--- + +# Internal `READ` Statement + +Internal `READ` does same as `DECODE` in F77 : **a cast from string** +> `READ (dev_no, format_label) var_list` +> read variables from internal storage specified by character variable used as `dev_no` = default character variable (not an array) + +```fortran +INTEGER K +REAL A,B +CHARACTER*80 REC80 +DATA REC80/'1.2, 2.3, -5'/ +... +READ(REC80,*) A,B,K +``` + +Results: + +```fortran +A=1.2, B=2.3, K=-5 +``` + +<!-- _footer: "" --> + +--- + +# Structured programming + +Structured programming is based on subprograms (functions and subroutines) and control statements (like `IF` statements or loops) : + +- structure the control-flow of your programs (e.g. give up the `GO TO`) +- improved readability +- lower level aspect of coding in a smart way + +It is a **programming paradigm** aimed at improving the quality, clarity, and access time of a computer program + +--- + +# Functions and Subroutines + +`FUNCTION` & `SUBROUTINE` are subprograms that allow structured coding + +- `FUNCTION`: returns a single explicit function value for given function arguments + It’s also a variable → so must be declared ! +- `SUBROUTINE`: any values returned must be returned through the arguments (no explicit subroutine value is returned) +- functions and subroutines are **not recursive in F77** + +Subprograms use a separate namespace for each subprogram so that variables are local to the subprogram + +- variables are passed to subprogram through argument list and returned in function value or through arguments +- variables stored in `COMMON` may be shared between namespaces + +<!-- _footer: "" --> + +--- + +# Functions and Subroutines - cont'd + +Subprograms must (should) include at least one `RETURN` (can have more) and be terminated by an `END` statement + +`FUNCTION` example: + +```fortran +REAL FUNCTION AVG3(A,B,C) +AVG3=(A+B+C)/3 +RETURN +END +``` + +Use: + +```fortran +AV = WEIGHT*AVG3(A1,F2,B2) +``` + +> `FUNCTION` type is implicitly defined as REAL + +--- + +# Functions and Subroutines - cont'd + +Subroutine is invoked using the `CALL` statement + +```fortran +SUBROUTINE AVG3S(A,B,C,AVERAGE) +AVERAGE=(A+B+C)/3 +RETURN +END +``` + +Use: + +```fortran +CALL AVG3S(A1,F2,B2,AVR) +RESULT = WEIGHT*AVR +``` + +Any returned values must be returned through argument list + +> Fortran 90 source code [newton.f90](https://gogs.elic.ucl.ac.be/pbarriat/learning-fortran/src/master/src/05_newton.f90) + +--- + +# Arguments + +Arguments in subprogram are `dummy` arguments used in place of the real arguments + +- arguments are passed by **reference** (memory address) if given as *symbolic* + >the subprogram can then alter the actual argument value since it can access it by reference +- arguments are passed by **value** if given as *literal* (so cannot be modified) + +```fortran +CALL AVG3S(A1,3.4,C1,QAV) +``` + +> 2nd argument is passed by value - QAV contains result + +```fortran +CALL AVG3S(A,C,B,4.1) +``` + +> no return value is available since "4.1" is a value and not a reference to a variable! + +--- + +# Arguments - cont'd + +- `dummy` arguments appearing in a subprogram declaration cannot be an individual array element reference, e.g., `A(2)`, or a *literal*, for obvious reasons! +- arguments used in invocation (by calling program) may be *variables*, *subscripted variables*, *array names*, *literals*, *expressions* or *function names* +- using symbolic arguments (variables or array names) is the **only way** to return a value (result) from a `SUBROUTINE` + +> It is considered **BAD coding practice**, but functions can return values by changing the value of arguments + This type of use should be strictly **avoided**! + +--- + +# Arguments - cont'd + +The `INTENT` keyword (>F90) increases readability and enables better compile-time error checking + +```fortran +SUBROUTINE AVG3S(A,B,C,AVERAGE) + IMPLICIT NONE + REAL, INTENT(IN) :: A, B + REAL, INTENT(INOUT) :: C ! default + REAL, INTENT(OUT) :: AVERAGE + + A = 10 ! Compilation error + C = 10 ! Correct + AVERAGE=(A+B+C)/3 ! Correct +END +``` + +> Compiler uses `INTENT` for error checking and optimization + +--- + +# `FUNCTION` versus Array + +`REMAINDER(4,3)` could be a 2D array or it could be a reference to a function + +If the name, including arguments, **matches an array declaration**, then it is taken to be an array, **otherwise**, it is assumed to be a `FUNCTION` + +Be careful about `implicit` versus `explicit` type declarations with `FUNCTION` + +```fortran +PROGRAM MAIN + INTEGER REMAINDER + ... + KR = REMAINDER(4,3) + ... +END + +INTEGER FUNCTION REMAINDER(INUM,IDEN) + ... +END +``` + +<!-- _footer: "" --> + +--- + +# Arrays with Subprograms + +Arrays present special problems in subprograms + +- must pass by reference to subprogram since there is no way to list array values explicitly as literals +- how do you tell subprogram how large the array is ? + +> Answer varies with FORTRAN version and vendor (dialect)... + +When an array element, e.g. `A(1)`, is used in a subprogram invocation (in calling program), it is passed as a reference (address), just like a simple variable + +When an array is used by name in a subprogram invocation (in calling program), it is passed as a reference to the entire array. In this case the array must be appropriately dimensioned in the subroutine (and this can be tricky...) + +--- + +# Arrays - cont'd + +### Data layout in multi-dimensional arrays + +- always increment the left-most index of multi-dimensional arrays in the innermost loop (i.e. fastest) +- **column major** ordering in Fortran vs. **row major** ordering in C +- a compiler (with sufficient optimization flags) may re-order loops automatically + +```fortran +do j=1,M + do i=1,N ! innermost loop + y(i) = y(i)+ a(i,j)*x(j) ! left-most index is i + end do +end do +``` + +--- + +# Arrays - cont'd + +- dynamically allocate memory for arrays using `ALLOCATABLE` on declaration +- memory is allocated through `ALLOCATE` statement in the code and is deallocated through `DEALLOCATE` statement + +```fortran +integer :: m, n +integer, allocatable :: idx(:) +real, allocatable :: mat(:,:) +m = 100 ; n = 200 +allocate( idx(0:m-1)) +allocate( mat(m, n)) +... +deallocate(idx , mat) +``` + +> It exists many array intrinsic functions: SIZE, SHAPE, SUM, ANY, MINVAL, MAXLOC, RESHAPE, DOT_PRODUCT, TRANSPOSE, WHERE, FORALL, etc + +--- + +# `COMMON` & `MODULE` Statement + +The `COMMON` statement allows variables to have a more extensive scope than otherwise + +- a variable declared in a `Main Program` can be made accessible to subprograms (without appearing in argument lists of a calling statement) +- this can be selective (don't have to share all everywhere) with `ONLY` +- **placement**: among type declarations, after `IMPLICIT` or `EXPLICIT`, before `DATA` statements +- can group into **labeled** `COMMON` + +With > F90, it's better to use the `MODULE` subprogram instead of the `COMMON` statement + +> Fortran 77 source code [common.f](https://gogs.elic.ucl.ac.be/pbarriat/learning-fortran/src/master/src/06_common.f) - Fortran 90 source code [module.f90](https://gogs.elic.ucl.ac.be/pbarriat/learning-fortran/src/master/src/06_module.f90) + +--- + +# Modular programming (>F90) + +Modular programming is about separating parts of programs into independent and interchangeable modules : + +- improve testability +- improve maintainability +- re-use of code +- higher level aspect of coding in a smart way +- *separation of concerns* + +The principle is that making significant parts of the code independent, replaceable and independently testable makes your programs **more maintainable** + +--- + +# Data Type Declarations + +FORTRAN >90 allows user derived types + +```fortran +TYPE my_variable + character(30) :: name + integer :: id + real(8) :: value + integer, dimension(3,3) :: dimIndex +END TYPE variable + +type(my_variable) var +var%name = "salinity" +var%id = 1 +``` + +--- + +# Subprograms type + +`MODULE` are subprograms that allow modular coding and data encapsulation + +The interface of a subprogram type is **explicit** or **implicit** + +Several types of subprograms: + +- `intrinsic`: explicit - defined by Fortran itself (trignonometric functions, etc) +- `module`: explicit - defined with `MODULE` statement and used with `USE` +- `internal`: explicit - defined with `CONTAINS` statement inside (sub)programs +- `external`: implicit (but can be manually (re)defined explicit) - e.g. **libraries** + +Differ with the **scope**: what data and other subprograms a subprogram can access + +--- + +# `MODULE` type + +```fortran +MODULE example + IMPLICIT NONE + INTEGER, PARAMETER :: index = 10 + REAL(8), SAVE :: latitude +CONTAINS + FUNCTION check(x) RESULT(z) + INTEGER :: x, z + ... + END FUNCTION check +END MODULE example +``` + +```fortran +PROGRAM myprog + USE example, ONLY: check, latitude + IMPLICIT NONE + ... + test = check(a) + ... +END PROGRAM myprog +``` + +<!-- _footer: "" --> + +<!-- Notes for presenter. --> +<!-- +```fortran +module subs + +contains + +subroutine asub (i, control) + + implicit none + + integer, intent (in) :: i + logical, intent (in) :: control + + integer, save :: j = 0 + integer :: k + + j = j + i + if ( control ) k = 0 + k = k + i + + write (*, *) 'i, j, k=', i, j, k + +end subroutine asub + +end module subs + +program test_saves + + use subs + implicit none + + call asub ( 3, .TRUE. ) + call asub ( 4, .FALSE. ) + +end program test_saves +``` + +Local variable k of the subroutine is intentionally misused -- in this program it is initialized in the first call since control is TRUE, but on the second call control is FALSE, so k is not redefined. But without the save attribute k is undefined, so the using its value is illegal. + +```fortran + i, j, k= 3 3 3 + i, j, k= 4 7 7 +``` + +Compiling the program with ifort and aggressive optimization options, k lost its value: + +```fortran + i, j, k= 3 3 3 + i, j, k= 4 7 4 +``` +--> + +--- + +# `internal` subprogams + +```fortran +program main + implicit none + integer N + real X(20) + ... + write(*,*), 'Processing x...', process() + ... +contains + logical function process() + ! in this function N and X can be accessed directly (scope of main) + ! Please not that this method is not recommended: + ! it would be better to pass X as an argument of process + implicit none + if (sum(x) > 5.) then + process = .FALSE. + else + process = .TRUE. + endif + end function process +end program +``` + +<!-- _footer: "" --> + +--- + +# `external` subprogams + +- `external` subprogams are defined in a separate program unit +- to use them in another program unit, refer with the `EXTERNAL` statement +- compiled separately and linked + +**!!! DO NOT USE THEM**: modules are much easier and more robust :exclamation: + +They are only needed when subprogams are written with different programming language or when using external libraries (such as BLAS) + +> It's **highly** recommended to construct `INTERFACE` blocks for any external subprogams used + +--- + +# `interface` statement + +```fortran +SUBROUTINE nag_rand(table) + INTERFACE + SUBROUTINE g05faf(a,b,n,x) + REAL, INTENT(IN) :: a, b + INTEGER, INTENT(IN) :: n + REAL, INTENT(OUT) :: x(n) + END SUBROUTINE g05faf + END INTERFACE + ! + REAL, DIMENSION(:), INTENT(OUT) :: table + ! + call g05faf(-1.0,-1.0, SIZE(table), table) +END SUBROUTINE nag_rand +``` + +<!-- _footer: "" --> + +--- + +# Fortran Compiler and libraries + +Examples: + +```bash +module load netCDF-Fortran/4.5.3-gompi-2021b +gfortran -ffree-line-length-none \ +-o OceanGrideChange.exe 07_OceanGrideChange.f90 \ +-I${EBROOTNETCDFMINFORTRAN}/include -L${EBROOTNETCDFMINFORTRAN}/lib -lnetcdff +``` + +```bash +module load netCDF-Fortran/4.5.3-iimpi-2021b +ifort -O3 \ +-o OceanGrideChange.exe 07_OceanGrideChange.f90 \ +-I${EBROOTNETCDFMINFORTRAN}/include -L${EBROOTNETCDFMINFORTRAN}/lib -lnetcdff +``` + +> Fortran 90 source code [OceanGrideChange.f90](https://gogs.elic.ucl.ac.be/pbarriat/learning-fortran/src/master/src/07_OceanGrideChange.f90) with the input file [input.nc](https://gogs.elic.ucl.ac.be/pbarriat/learning-fortran/src/master/src/07_input.nc) + +--- + +# Conclusions + +- Fortran in all its standard versions and vendor-specific dialects is a rich but confusing language +- Fortran is a modern language that continues to evolve + +- Fortran is still ideally suited for numerical computations in engineering and science + - most new language features have been added since F95 + - "High Performance Fortran" includes capabilities designed for parallel processing diff --git a/slides.pdf b/slides.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1ced077d3a59720207acfa6e17ce3b9820974916 Binary files /dev/null and b/slides.pdf differ diff --git a/src/solutions/08_ChristmasTree.f90 b/src/solutions/08_ChristmasTree.f90 deleted file mode 100644 index e6ec7a26417d86ec322d27a2b888b10de666776d..0000000000000000000000000000000000000000 --- a/src/solutions/08_ChristmasTree.f90 +++ /dev/null @@ -1,34 +0,0 @@ -program ChristmasTree - - implicit none - - integer i, h, hmax, line, ball - character(8) hmax_string - character(1) sball - - ! get the command line argument - call getarg(1,hmax_string) - - ! cast string to integer - read(hmax_string,*) hmax - - ball=1 - do h=1,hmax - line=1 - ! write spaces to align the head of the tree - write(*,'(a)',advance='no') repeat(' ',hmax-h) - ! loop to decide when we have to create a new line - do while(line.le.(2*h-1)) - ! modulo to decide when we have to put a ball or not - sball='#' - if(mod(ball,6).eq.0) sball='o' - write(*,'(A)',advance='no') sball - ! increment ball/line decision variables - ball=ball+1 - line=line+1 - enddo - ! create a new line - write(*,*) - enddo - -end program