From 6402f322be5f49a5d022878c6c8a1f7774932ee4 Mon Sep 17 00:00:00 2001 From: Luke Abraham Date: Thu, 31 Oct 2024 14:44:49 +0000 Subject: [PATCH 01/26] initial commit covering Fortran data types --- config.yaml | 1 + episodes/02-basics.Rmd | 330 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 331 insertions(+) create mode 100644 episodes/02-basics.Rmd diff --git a/config.yaml b/config.yaml index 62d6b956..05624458 100644 --- a/config.yaml +++ b/config.yaml @@ -61,6 +61,7 @@ contact: 'd.theodorakis@metoffice.gov.uk' # Order of episodes in your lesson episodes: - introduction.Rmd +- 02-basics.Rmd # Information for Learners learners: diff --git a/episodes/02-basics.Rmd b/episodes/02-basics.Rmd new file mode 100644 index 00000000..dd5ede90 --- /dev/null +++ b/episodes/02-basics.Rmd @@ -0,0 +1,330 @@ +--- +title: 'Basics' +teaching: 35 +exercises: 15 +--- + +::::::::::::::::::::::::::::::::::::: questions + +- How do we represent and manipulate data in Fortran? + +:::::::::::::::::::::::::::::::::::::::::::::::: + +::::::::::::::::::::::::::::::::::::: objectives + +- Understand the different intrinsic data types +- Understand the different intrinsic operators and functions available + +:::::::::::::::::::::::::::::::::::::::::::::::: + +## Fortran data types + +Fortran provides the following intrinsic data types: + +* numeric types + * `integer` + * `real` + * `complex` +* non-numeric types + * `logical` + * `character` + +### Defining numeric variables + +The following program declares a variable with each of the three intrinsic +numeric types, and provides an initial value in each case. +```fortran +program example1 + + integer :: i = 1 ! An integer + real :: a = 2.0 ! A floating point number + complex :: z = (0.0, 1.0) ! A complex number as (real-part, imag-part) + +end program example1 +``` +Initial values are optional, and these values may be changed later within the code. +If a declaration does not specify an initial value, the variable is said to be _undefined_. + +### Variable names + +The valid Fortran character set for names is `a-z`, `A-Z`, `0-9` and the +underscore `_`. Valid names must begin with a character. The maximum +length of a name is 63 characters (introduced in the F2003 standard) with +no spaces allowed. This includes names for programs, modules, subroutines, +and functions, as well as names for variables. + +### The `implicit` statement + +By default, variables in fortran do not need to be declared a specific type, they +can just be used within the code. Variables with names beginning with letters `i-n` are implicitly +of type `integer`, while anything else is of type `real` (unless explicitly declared otherwise). + +**This is very bad practice and modern Fortran should not be used in this way.** + +The solution to prevent errors involving undeclared variables (usually arising from +typos) is to declare that no names have implicit type via the use of the +```fortran +implicit none +``` +statment at the very top of the code. + +With this statement inserted, all variable names must be declared explicitly before they +are referenced. It is still common to see variables beginning with `i-n` as integers. + +::::::::::::::::::::::::::::::::::::: challenge + +## Use `implicit none` + +Edit the above example to include an `implicit none` statment, and print out the three variables + +:::::::::::::::: solution + +```fortran +program solution1 + implicit none + + integer :: i = 1 ! An integer + real :: a = 2.0 ! A floating point number + complex :: z = (0.0, 1.0) ! A complex number as (real-part, imag-part) + + print *, i, a, z + +end program solution1 +``` +Compiling and running this code will give the following output +``` +$ ./a.out + 1 2.00000000 (0.00000000,1.00000000) +``` + +::::::::::::::::::::::::: +::::::::::::::::::::::::::::::::::::::::::::::: + +### Parameters + +In the above example, it is possible to change the values of initialised variables, e.g. +```fortran +program example2 + implicit none + + real :: a = 2.0 + + print *, a + + a = a + 5.0 + + print *, a + +end program example2 +``` +This will then give the output: +``` +$ ./a.out + 2.00000000 + 7.00000000 +``` +However, you can also define constant values that do not change by defining variables using `parameter`, e.g. +```fortran +program example3 + implicit none + + real, parameter :: pi = 3.14159265 + +end program example3 +``` + +::::::::::::::::::::::::::::::::::::: challenge + +## Write a Fortran program to calculate the circumference of a circle + +Using the `parameter` statement, write a Fortran program to calculate the circumference of a circle of +radius 3.0. + +::::::::::::::::: hint + +The cicumference $c$ of a circle can be calculated from the radius $r$ and constant $\pi$ by + +$c = 2 \pi r$ + +:::::::::::::::::::::: + +:::::::::::::::: solution + +```fortran +program solution2 + implicit none + + real, parameter :: pi = 3.14159265 + + real :: r=3.0 ! radius of the circle + real :: c ! circumference of the circle + + c = 2.0 * pi * r + + print *, c + +end program solution2 +``` +Compiling and running this code will give the following output +``` +$ ./a.out + 18.8495560 +``` + +::::::::::::::::::::::::: +::::::::::::::::::::::::::::::::::::::::::::::: + +## Logical variables + +Fortran has a `logical` type that has two literal values, True and False, defined by +```fortran + logical :: switch0 = .false. + logical :: switch1 = .true. +``` + +### Logical operators and expressions + +Values can be tested logical operators `.or.`, `.and.` and `.not.` are available, and +these can be used to set the value of logical variables. + +The precedence is illustrated by, e.g., +```fortran + q = i .or. j .and. .not. k ! evaluated as i .or. (j .and. (.not. k)) +``` +where q, i, j, and k are all logical variables. + +The use of parentheses is highly recommended to avoid ambiguity and/or to add clarity. + +### Relational operators + +To form logical expressions from numeric or other expressions, we require +relational operators. The are two forms in Fortran, illustrated in the table +below. It is recommended that you avoid the older form. + +| Relation | Operator | Older form | For | +|--------------------------|----------|------------|------------------| +| Less than | `< ` | `.lt.` | `integer` `real` | +| Less than or equal to | `<=` | `.le.` | `integer` `real` | +| Greater than | `> ` | `.gt.` | `integer` `real` | +| Greater than or equal to | `>=` | `.ge.` | `integer` `real` | +| Equal to | `==` | `.eq.` | `integer` `real` `complex`| +| Not equal to | `/=` | `.neq.` | `integer` `real` `complex`| + +### Logical equivalence + +Equivalence between two logical expressions or variables is established +via the logical operators `.eqv.` and `.neqv.`. + +While some some compilers may allow the use of `==`, this should be avoided. + +### Using logical operators + +These operators can be used to check and set the values of logical variables, dependent on other variables, e.g. +```fortran +program example4 + implicit none + + real, parameter :: pi = 3.14159265 + logical, parameter :: switch1 = .true. + + real :: a=3.0 + logical :: test1, test2, test3 + + test1 = a >= pi ! True if a is greater than or equal to pi + + test2 = (.not. test1) ! True if test1 is False, False if test1 is True + + test3 = (test2 .eqv. switch1) ! True if test2 is True, False if test2 is False + + print *, test1 + + print *, test2 + + print *, test3 + +end program example4 +``` +Compiling and running this code will give the following output +``` +$ ./a.out + F + T + T +``` + +## Character variables + +Character variables are strings that hold zero or more characters. Some examples are: +```fortran +program example5 + implicit none + + character (len = *), parameter :: string1 = "don't" ! assumed length of 5 characters + character (len = 5) :: string2 = "Don""t" ! 5 characters + character (len = 6) :: string3 = ' don''t' ! blank + 5 characters + +end program example5 +``` +There should be a `len` specifier. Specifying `len = *` means that the length will be assumed +from the length of the string. A space is a valid character in a string. + +Strings must be defined within quotation marks, either single, `'`, or double, `"`, beginning +and ending with the same type of mark. To define a quotation mark within a string, either +use one of the alternative type, e.g. `"'"`, or use two of the same type, e.g. `''''`, which +will be intepreted as a single mark within the string. + +Strings may be concatenated with the string concatenation operator `//`. For example `string2` +and `string3` could be concatenated into a new variable `string4` by +```fortran + string4 = string2//string3 +``` +A single character, or a subset of characters, can be extracted via +use of an array-index like notation by defining the start and end characters +to extract, e.g. `string1(1:1)` would extract only the first character from the string +variable `string1`. + +::::::::::::::::::::::::::::::::::::: challenge + +## Working with strings + +Using the above `example5`, concatinate `string2` and `string3` into a new variable `string4`, +and print out the values of all strings. Also, print the third character of `string4`. + +:::::::::::::::: solution + +```fortran +program solution3 + implicit none + + character (len = *), parameter :: string1 = "don't" ! 5 characters + character (len = 5) :: string2 = "Don""t" ! 5 characters + character (len = 6) :: string3 = ' don''t' ! blank + 5 characters + character (len = 11) :: string4 ! length of string2+string3 + + string4 = string2//string3 + + print *, string1 + + print *, string2 + + print *, string3 + + print *, string4 + + print *, string4(3:3) + +end program solution3 +``` +Compiling and running this code will give the following output +``` +$ ./a.out + don't + Don"t + don't + Don"t don't + n +``` + +::::::::::::::::::::::::: +::::::::::::::::::::::::::::::::::::::::::::::: + From 3744b873097751f2047e6e987c2356d537e31d44 Mon Sep 17 00:00:00 2001 From: Luke Abraham Date: Wed, 20 Nov 2024 10:32:58 +0000 Subject: [PATCH 02/26] Update episodes/02-basics.Rmd Co-authored-by: Warrick Ball --- episodes/02-basics.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/episodes/02-basics.Rmd b/episodes/02-basics.Rmd index dd5ede90..b5876804 100644 --- a/episodes/02-basics.Rmd +++ b/episodes/02-basics.Rmd @@ -55,7 +55,7 @@ and functions, as well as names for variables. ### The `implicit` statement -By default, variables in fortran do not need to be declared a specific type, they +By default, variables in Fortran do not need to be declared a specific type, they can just be used within the code. Variables with names beginning with letters `i-n` are implicitly of type `integer`, while anything else is of type `real` (unless explicitly declared otherwise). From 900449f7bdf3a295229130940332f89883907db7 Mon Sep 17 00:00:00 2001 From: Luke Abraham Date: Wed, 20 Nov 2024 10:33:16 +0000 Subject: [PATCH 03/26] Update episodes/02-basics.Rmd Co-authored-by: Warrick Ball --- episodes/02-basics.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/episodes/02-basics.Rmd b/episodes/02-basics.Rmd index b5876804..b61667c3 100644 --- a/episodes/02-basics.Rmd +++ b/episodes/02-basics.Rmd @@ -287,7 +287,7 @@ variable `string1`. ## Working with strings -Using the above `example5`, concatinate `string2` and `string3` into a new variable `string4`, +Using the above `example5`, concatenate `string2` and `string3` into a new variable `string4`, and print out the values of all strings. Also, print the third character of `string4`. :::::::::::::::: solution From fcb2bf3a281c64a79825d584023154eacf0340e2 Mon Sep 17 00:00:00 2001 From: Luke Abraham Date: Mon, 2 Dec 2024 16:19:58 +0000 Subject: [PATCH 04/26] added error message when trying to alter a parameter --- episodes/02-basics.Rmd | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/episodes/02-basics.Rmd b/episodes/02-basics.Rmd index b61667c3..46bbdab4 100644 --- a/episodes/02-basics.Rmd +++ b/episodes/02-basics.Rmd @@ -123,15 +123,28 @@ $ ./a.out 2.00000000 7.00000000 ``` -However, you can also define constant values that do not change by defining variables using `parameter`, e.g. +However, you can also define constant values that cannot change by defining variables using `parameter`. +We are then unable to modify `parameter` variables, e.g. ```fortran program example3 implicit none real, parameter :: pi = 3.14159265 + pi = pi + 2.0 + + print *, pi + end program example3 ``` +When compiling this program, this will give an error similar to the following from the `gfortran` compiler: +``` +example3.f90:6:3: + + 6 | pi = pi + 2.0 + | 1 +Error: Named constant 'pi' in variable definition context (assignment) at (1) +``` ::::::::::::::::::::::::::::::::::::: challenge From e20bcb7eee3cb7002a758a6c084d156a0321f545 Mon Sep 17 00:00:00 2001 From: Luke Abraham Date: Thu, 31 Oct 2024 14:44:49 +0000 Subject: [PATCH 05/26] initial commit covering Fortran data types --- config.yaml | 1 + episodes/02-basics.Rmd | 330 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 331 insertions(+) create mode 100644 episodes/02-basics.Rmd diff --git a/config.yaml b/config.yaml index 0a64b1ab..ffedadd8 100644 --- a/config.yaml +++ b/config.yaml @@ -61,6 +61,7 @@ contact: 'd.theodorakis@metoffice.gov.uk' # Order of episodes in your lesson episodes: - introduction.Rmd +- 02-basics.Rmd # Information for Learners learners: diff --git a/episodes/02-basics.Rmd b/episodes/02-basics.Rmd new file mode 100644 index 00000000..dd5ede90 --- /dev/null +++ b/episodes/02-basics.Rmd @@ -0,0 +1,330 @@ +--- +title: 'Basics' +teaching: 35 +exercises: 15 +--- + +::::::::::::::::::::::::::::::::::::: questions + +- How do we represent and manipulate data in Fortran? + +:::::::::::::::::::::::::::::::::::::::::::::::: + +::::::::::::::::::::::::::::::::::::: objectives + +- Understand the different intrinsic data types +- Understand the different intrinsic operators and functions available + +:::::::::::::::::::::::::::::::::::::::::::::::: + +## Fortran data types + +Fortran provides the following intrinsic data types: + +* numeric types + * `integer` + * `real` + * `complex` +* non-numeric types + * `logical` + * `character` + +### Defining numeric variables + +The following program declares a variable with each of the three intrinsic +numeric types, and provides an initial value in each case. +```fortran +program example1 + + integer :: i = 1 ! An integer + real :: a = 2.0 ! A floating point number + complex :: z = (0.0, 1.0) ! A complex number as (real-part, imag-part) + +end program example1 +``` +Initial values are optional, and these values may be changed later within the code. +If a declaration does not specify an initial value, the variable is said to be _undefined_. + +### Variable names + +The valid Fortran character set for names is `a-z`, `A-Z`, `0-9` and the +underscore `_`. Valid names must begin with a character. The maximum +length of a name is 63 characters (introduced in the F2003 standard) with +no spaces allowed. This includes names for programs, modules, subroutines, +and functions, as well as names for variables. + +### The `implicit` statement + +By default, variables in fortran do not need to be declared a specific type, they +can just be used within the code. Variables with names beginning with letters `i-n` are implicitly +of type `integer`, while anything else is of type `real` (unless explicitly declared otherwise). + +**This is very bad practice and modern Fortran should not be used in this way.** + +The solution to prevent errors involving undeclared variables (usually arising from +typos) is to declare that no names have implicit type via the use of the +```fortran +implicit none +``` +statment at the very top of the code. + +With this statement inserted, all variable names must be declared explicitly before they +are referenced. It is still common to see variables beginning with `i-n` as integers. + +::::::::::::::::::::::::::::::::::::: challenge + +## Use `implicit none` + +Edit the above example to include an `implicit none` statment, and print out the three variables + +:::::::::::::::: solution + +```fortran +program solution1 + implicit none + + integer :: i = 1 ! An integer + real :: a = 2.0 ! A floating point number + complex :: z = (0.0, 1.0) ! A complex number as (real-part, imag-part) + + print *, i, a, z + +end program solution1 +``` +Compiling and running this code will give the following output +``` +$ ./a.out + 1 2.00000000 (0.00000000,1.00000000) +``` + +::::::::::::::::::::::::: +::::::::::::::::::::::::::::::::::::::::::::::: + +### Parameters + +In the above example, it is possible to change the values of initialised variables, e.g. +```fortran +program example2 + implicit none + + real :: a = 2.0 + + print *, a + + a = a + 5.0 + + print *, a + +end program example2 +``` +This will then give the output: +``` +$ ./a.out + 2.00000000 + 7.00000000 +``` +However, you can also define constant values that do not change by defining variables using `parameter`, e.g. +```fortran +program example3 + implicit none + + real, parameter :: pi = 3.14159265 + +end program example3 +``` + +::::::::::::::::::::::::::::::::::::: challenge + +## Write a Fortran program to calculate the circumference of a circle + +Using the `parameter` statement, write a Fortran program to calculate the circumference of a circle of +radius 3.0. + +::::::::::::::::: hint + +The cicumference $c$ of a circle can be calculated from the radius $r$ and constant $\pi$ by + +$c = 2 \pi r$ + +:::::::::::::::::::::: + +:::::::::::::::: solution + +```fortran +program solution2 + implicit none + + real, parameter :: pi = 3.14159265 + + real :: r=3.0 ! radius of the circle + real :: c ! circumference of the circle + + c = 2.0 * pi * r + + print *, c + +end program solution2 +``` +Compiling and running this code will give the following output +``` +$ ./a.out + 18.8495560 +``` + +::::::::::::::::::::::::: +::::::::::::::::::::::::::::::::::::::::::::::: + +## Logical variables + +Fortran has a `logical` type that has two literal values, True and False, defined by +```fortran + logical :: switch0 = .false. + logical :: switch1 = .true. +``` + +### Logical operators and expressions + +Values can be tested logical operators `.or.`, `.and.` and `.not.` are available, and +these can be used to set the value of logical variables. + +The precedence is illustrated by, e.g., +```fortran + q = i .or. j .and. .not. k ! evaluated as i .or. (j .and. (.not. k)) +``` +where q, i, j, and k are all logical variables. + +The use of parentheses is highly recommended to avoid ambiguity and/or to add clarity. + +### Relational operators + +To form logical expressions from numeric or other expressions, we require +relational operators. The are two forms in Fortran, illustrated in the table +below. It is recommended that you avoid the older form. + +| Relation | Operator | Older form | For | +|--------------------------|----------|------------|------------------| +| Less than | `< ` | `.lt.` | `integer` `real` | +| Less than or equal to | `<=` | `.le.` | `integer` `real` | +| Greater than | `> ` | `.gt.` | `integer` `real` | +| Greater than or equal to | `>=` | `.ge.` | `integer` `real` | +| Equal to | `==` | `.eq.` | `integer` `real` `complex`| +| Not equal to | `/=` | `.neq.` | `integer` `real` `complex`| + +### Logical equivalence + +Equivalence between two logical expressions or variables is established +via the logical operators `.eqv.` and `.neqv.`. + +While some some compilers may allow the use of `==`, this should be avoided. + +### Using logical operators + +These operators can be used to check and set the values of logical variables, dependent on other variables, e.g. +```fortran +program example4 + implicit none + + real, parameter :: pi = 3.14159265 + logical, parameter :: switch1 = .true. + + real :: a=3.0 + logical :: test1, test2, test3 + + test1 = a >= pi ! True if a is greater than or equal to pi + + test2 = (.not. test1) ! True if test1 is False, False if test1 is True + + test3 = (test2 .eqv. switch1) ! True if test2 is True, False if test2 is False + + print *, test1 + + print *, test2 + + print *, test3 + +end program example4 +``` +Compiling and running this code will give the following output +``` +$ ./a.out + F + T + T +``` + +## Character variables + +Character variables are strings that hold zero or more characters. Some examples are: +```fortran +program example5 + implicit none + + character (len = *), parameter :: string1 = "don't" ! assumed length of 5 characters + character (len = 5) :: string2 = "Don""t" ! 5 characters + character (len = 6) :: string3 = ' don''t' ! blank + 5 characters + +end program example5 +``` +There should be a `len` specifier. Specifying `len = *` means that the length will be assumed +from the length of the string. A space is a valid character in a string. + +Strings must be defined within quotation marks, either single, `'`, or double, `"`, beginning +and ending with the same type of mark. To define a quotation mark within a string, either +use one of the alternative type, e.g. `"'"`, or use two of the same type, e.g. `''''`, which +will be intepreted as a single mark within the string. + +Strings may be concatenated with the string concatenation operator `//`. For example `string2` +and `string3` could be concatenated into a new variable `string4` by +```fortran + string4 = string2//string3 +``` +A single character, or a subset of characters, can be extracted via +use of an array-index like notation by defining the start and end characters +to extract, e.g. `string1(1:1)` would extract only the first character from the string +variable `string1`. + +::::::::::::::::::::::::::::::::::::: challenge + +## Working with strings + +Using the above `example5`, concatinate `string2` and `string3` into a new variable `string4`, +and print out the values of all strings. Also, print the third character of `string4`. + +:::::::::::::::: solution + +```fortran +program solution3 + implicit none + + character (len = *), parameter :: string1 = "don't" ! 5 characters + character (len = 5) :: string2 = "Don""t" ! 5 characters + character (len = 6) :: string3 = ' don''t' ! blank + 5 characters + character (len = 11) :: string4 ! length of string2+string3 + + string4 = string2//string3 + + print *, string1 + + print *, string2 + + print *, string3 + + print *, string4 + + print *, string4(3:3) + +end program solution3 +``` +Compiling and running this code will give the following output +``` +$ ./a.out + don't + Don"t + don't + Don"t don't + n +``` + +::::::::::::::::::::::::: +::::::::::::::::::::::::::::::::::::::::::::::: + From abdb0ab839cd02d55124f9681e837e9f1d23e213 Mon Sep 17 00:00:00 2001 From: Luke Abraham Date: Wed, 20 Nov 2024 10:32:58 +0000 Subject: [PATCH 06/26] Update episodes/02-basics.Rmd Co-authored-by: Warrick Ball --- episodes/02-basics.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/episodes/02-basics.Rmd b/episodes/02-basics.Rmd index dd5ede90..b5876804 100644 --- a/episodes/02-basics.Rmd +++ b/episodes/02-basics.Rmd @@ -55,7 +55,7 @@ and functions, as well as names for variables. ### The `implicit` statement -By default, variables in fortran do not need to be declared a specific type, they +By default, variables in Fortran do not need to be declared a specific type, they can just be used within the code. Variables with names beginning with letters `i-n` are implicitly of type `integer`, while anything else is of type `real` (unless explicitly declared otherwise). From 0af6e5c6ede435bae2c42f958bebf8a74ba4a5b3 Mon Sep 17 00:00:00 2001 From: Luke Abraham Date: Wed, 20 Nov 2024 10:33:16 +0000 Subject: [PATCH 07/26] Update episodes/02-basics.Rmd Co-authored-by: Warrick Ball --- episodes/02-basics.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/episodes/02-basics.Rmd b/episodes/02-basics.Rmd index b5876804..b61667c3 100644 --- a/episodes/02-basics.Rmd +++ b/episodes/02-basics.Rmd @@ -287,7 +287,7 @@ variable `string1`. ## Working with strings -Using the above `example5`, concatinate `string2` and `string3` into a new variable `string4`, +Using the above `example5`, concatenate `string2` and `string3` into a new variable `string4`, and print out the values of all strings. Also, print the third character of `string4`. :::::::::::::::: solution From bef78772f0cbe3800568fc32ce2d0297f4944cb7 Mon Sep 17 00:00:00 2001 From: Luke Abraham Date: Mon, 2 Dec 2024 16:19:58 +0000 Subject: [PATCH 08/26] added error message when trying to alter a parameter --- episodes/02-basics.Rmd | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/episodes/02-basics.Rmd b/episodes/02-basics.Rmd index b61667c3..46bbdab4 100644 --- a/episodes/02-basics.Rmd +++ b/episodes/02-basics.Rmd @@ -123,15 +123,28 @@ $ ./a.out 2.00000000 7.00000000 ``` -However, you can also define constant values that do not change by defining variables using `parameter`, e.g. +However, you can also define constant values that cannot change by defining variables using `parameter`. +We are then unable to modify `parameter` variables, e.g. ```fortran program example3 implicit none real, parameter :: pi = 3.14159265 + pi = pi + 2.0 + + print *, pi + end program example3 ``` +When compiling this program, this will give an error similar to the following from the `gfortran` compiler: +``` +example3.f90:6:3: + + 6 | pi = pi + 2.0 + | 1 +Error: Named constant 'pi' in variable definition context (assignment) at (1) +``` ::::::::::::::::::::::::::::::::::::: challenge From 826f6690ea97c6109822bb127226352c3b3e3147 Mon Sep 17 00:00:00 2001 From: Dimitrios Theodorakis Date: Wed, 23 Apr 2025 15:34:41 +0100 Subject: [PATCH 09/26] Splits off the start of the variables lesson into its own file - add a solutions directory. --- episodes/02-basics-variables.md | 461 ++++++++++++++++++ .../solutions/02_variables/variables.f90 | 30 ++ 2 files changed, 491 insertions(+) create mode 100644 episodes/02-basics-variables.md create mode 100644 exercises/solutions/02_variables/variables.f90 diff --git a/episodes/02-basics-variables.md b/episodes/02-basics-variables.md new file mode 100644 index 00000000..2c19af55 --- /dev/null +++ b/episodes/02-basics-variables.md @@ -0,0 +1,461 @@ +--- +title: 'Variables' +teaching: 15 +exercises: 15 +--- + +::::::::::::::::::::::::::::::::::::: questions + +- How do we declare and assign values to variables? + +:::::::::::::::::::::::::::::::::::::::::::::::: + +::::::::::::::::::::::::::::::::::::: objectives + +- Understand the different intrinsic data types. +- Declare and assign variables and parameters. + +:::::::::::::::::::::::::::::::::::::::::::::::: + +Variables store information we can use in our programs. +Fortran is a ***strongly typed*** language. +Each variable must be declared with a type.[^static] +Fortran provides the following intrinsic (built-in) data types for variables: + +- `integer` +- `real` +- `complex` +- `logical` +- `character` + +Variable names must be 63 characters or less (F2003 standard). +No spaces are allowed. +Names can contain the characters `a-z`, `A-Z`, `0-9` and the underscore `_`. +No spaces are allowed. +Names must begin with a letter. +This also applies to names for programs, modules, subroutines, +and functions, which you will learn about later in the course. + +[^static]: Fortran is also ***statically typed***. + You can not change a variables type after the variable declaration. + +## Declaring Variables + +Fortran variables are declared with this syntax: + +```fortran + :: +``` + +So to declare an integer: + +```fortran +integer :: number_of_pelicans +``` + +:::: challenge + +1. Create a new Fortran program named `variables.f90`. +2. Declare 5 variables. One of each of the 5 intrinsic data types. +3. Print your variables. +4. Compile and run your program. + +What do you notice about the output? + +::: solution + +```fortran +program variables + + implicit none + + integer :: number_of_pelicans + real :: pelican_weight + complex :: pelican_population_dynamics + logical :: is_young_pelican + character :: pelican_tag + + print *, number_of_pelicans + print *, pelican_weight + print *, pelican_population_dynamics + print *, is_young_pelican + print *, pelican_tag + +end program variables + +``` + +::: group-tab + +### GFortran + +```bash +gfortran -o variables variables.f90 +``` + +### Intel + +```bash +ifx -o variables variables.f90 +``` + +### Flang + +```bash +flang -o variables variables.f90 +``` + +### Cray + +```bash +ftn -o variables variables.f90 +``` + +::: + +```bash +./variables +``` + +The output below is from the GNU gfortran compiled executable: + +```output + -922534656 + 0.00000000 + (2.063298560E+11,0.00000000) + T +``` + +Where did those values come from? +If you forget to assign a value to a variable +the output will depend on your compiler. +Here the program accessed the memory allocated for each variable +and printed what was leftover in the memory from other processes. + +Most compilers have a flag which warns you +if there are uninitialised variables in your code. +Have a look and see if there is a flag like this for your compiler. + +::: +:::: + +::: spoiler + +### Style + +- Fortran is case-insensitive. + This course prefers the use of lowercase. +- Fortran file names must match the name of the program or module + contained in the file. + ie. `variables.f90` contains the program `variables`. + `matrix_mod.f90` contains the module `matrix_mod`. +- `::` markers should be aligned to improve readability. + +::: + +## Variable Assignment + +Variables are assigned using the assignment operator `=`: + +```fortran + = +``` + +For example: + +```fortran +number_of_pelicans = 5 +pelican_weight = 2.5 ! kg +pelican_population_dynamics = (-1.2, 0.9) +is_young_pelican = .false. +pelican_tag = 'Jeff' +``` + +Logicals can be `.true.` or `.false.`. +Characters (strings) such as `pelican_tag` can be surrounded +by single or double quotes. + +:::: challenge + +1. Assign values to the variables in your program. +2. Compile and run your program. + +::: solution + +```fortran +program variables + + implicit none + + integer :: number_of_pelicans + real :: pelican_weight + complex :: pelican_population_dynamics + logical :: is_young_pelican + character :: pelican_tag + + number_of_pelicans = 5 + pelican_weight = 2.5 ! kg + pelican_population_dynamics = (-1.2, 0.9) + is_young_pelican = .false. + pelican_tag = 'J' + + print *, number_of_pelicans + print *, pelican_weight + print *, pelican_population_dynamics + print *, is_young_pelican + print *, pelican_tag + +end program variables + +``` + +::: group-tab + +### GFortran + +```bash +gfortran -o variables variables.f90 +``` + +### Intel + +```bash +ifx -o variables variables.f90 +``` + +### Flang + +```bash +flang -o variables variables.f90 +``` + +### Cray + +```bash +ftn -o variables variables.f90 +``` + +::: + +```bash +./variables +``` + +Example output: + +```output + 5 + 2.50000000 + (-1.20000005,0.899999976) + F + J +``` + +::: +:::: + +::: spoiler + +### Style + +- The assignment operator `=` should be aligned to improve readability. +- Variable names are written in snake case and are verbose. +- Variables with units must have a comment (Ford or vanilla Fortran style) with the unit. + +::: + +::: caution + +### Assignment on declaration + +Fortran lets you assign a value to a variable when you declare it. + +```fortran +integer :: number_of_pelicans = 5 +``` + +This gives the variable the `save` attribute. +With `save` the variable will keep its value between procedure (function) calls. +This is not good practice. +Never assign a value to a variable on declaration unless it's a parameter. + +::: + +## Parameters + +In your program, it is possible to change the values of initialised variables, e.g. + +```fortran +program variables + + implicit none + + integer :: number_of_pelicans + real :: pelican_weight + complex :: pelican_population_dynamics + logical :: is_young_pelican + character :: pelican_tag + + number_of_pelicans = 5 + pelican_weight = 2.5 ! kg + pelican_population_dynamics = (-1.2, 0.9) + is_young_pelican = .false. + pelican_tag = 'J' + + ! print *, number_of_pelicans + ! print *, pelican_weight + ! print *, pelican_population_dynamics + ! print *, is_young_pelican + print *, pelican_tag + + ! Changing the value of the tag + pelican_tag = 'F' + print *, pelican_tag + +end program variables + +``` + +This will now give the output: + +```output + J + F +``` + +However, you can also define constant values that cannot change. +You do this by defining variables using `parameter`. +We are then unable to modify `parameter` variables, e.g. + +```fortran +character, parameter :: pelican_tag = 'J' +``` + +:::: challenge + +1. Modify a variable in your code to be a parameter. +2. Try modifying the parameter in your code. + What output do you get when compiling? + +::: solution + +```fortran +program variables + + implicit none + + integer :: number_of_pelicans + real :: pelican_weight + complex :: pelican_population_dynamics + logical :: is_young_pelican + + character, parameter :: pelican_tag = 'J' + + number_of_pelicans = 5 + pelican_weight = 2.5 ! kg + pelican_population_dynamics = (-1.2, 0.9) + is_young_pelican = .false. + ! pelican_tag = 'J' + + print *, number_of_pelicans + print *, pelican_weight + print *, pelican_population_dynamics + print *, is_young_pelican + print *, pelican_tag + + pelican_tag = 'F' + +end program variables + +``` + +Here we have modified `pelican_tag` to be the parameter. +Then at the end of the program we attempt to change its value. + +Example GFortran output: + +```output +variables.f90:23:4: + + 23 | pelican_tag = 'F' + | 1 +Error: Named constant ‘pelican_tag’ in variable definition context (assignment) at (1) +``` + +The compiler has given us an error. +This is because we are trying to edit the value, `variable definition context (assignment)`, of a parameter, `Named constant`. +The error is in the `variables.f90` file, +on line `23`, starting at character `4`. +This location has been marked as `1` in the compiler output. + +Different compilers will show different error messages. +Some have clearer messages for certain errors than others. +We recommend testing code with **at least** two compilers. +This will aid your debugging and help make your code more portable. + +::: +:::: + +:::: challenge + +1. Make sure your code conforms to the style +followed by this course. +2. Add Ford comments to document the program +and each variable. + +::: solution + +```fortran +program variables + !! A test program to lean how to declare and assign variables. + + implicit none + + integer :: number_of_pelicans + !! The number of pelicans in the pod + real :: pelican_weight + !! The average weight of a pelican in the pod / kg + complex :: pelican_population_dynamics + !! The birth and death rate as a complex number + !! Units are the number of pelicans per year + logical :: is_young_pelican + !! Test to see if the current pelican is young + + character, parameter :: pelican_tag = 'J' + !! Pelican pod tracking tag + + number_of_pelicans = 5 + pelican_weight = 2.5 ! kg + pelican_population_dynamics = (-1.2, 0.9) ! births, deaths per year + is_young_pelican = .false. + + print *, number_of_pelicans + print *, pelican_weight + print *, pelican_population_dynamics + print *, is_young_pelican + print *, pelican_tag + +end program variables + +``` + +Notice we have left an extra blank line in-between the parameter +declaration and the other variable declarations. +This is so we didn't have to align all the `::` markers far to the right. +If you are declaring lots of variables, +break up the declarations into sections for readability. + +::: +:::: + +:::::::::::::::::::::::::::::::::::::::: keypoints + +- There are 5 intrinsic data types for Fortran variables: `integer`, `real`, `complex`, `logical`, and `character`. +- Fortran variables are declared with the syntax: ` :: ` +- Assign a value to a variable with the syntax: ` = ` +- Never assign a value on the same line as a variable is declared. + This gives the variable the `save` attribute. +- Parameters are variables whose value can't be changed: + `, parameter :: = `. + +:::::::::::::::::::::::::::::::::::::::::::::::::: diff --git a/exercises/solutions/02_variables/variables.f90 b/exercises/solutions/02_variables/variables.f90 new file mode 100644 index 00000000..783c5dae --- /dev/null +++ b/exercises/solutions/02_variables/variables.f90 @@ -0,0 +1,30 @@ +!-------------------------------------------------------- +! Carpentries - Introduction to Modern Fortran +! CC BY 4.0, https://creativecommons.org/licenses/by/4.0/ +!-------------------------------------------------------- +! +program variables + + implicit none + + integer :: number_of_pelicans + real :: pelican_weight + complex :: pelican_population_dynamics + logical :: is_young_pelican + character, parameter :: pelican_tag = 'J' + + number_of_pelicans = 5 + pelican_weight = 2.5 ! kg + pelican_population_dynamics = (-1.2, 0.9) + is_young_pelican = .false. + ! pelican_tag = 'J' + + print *, number_of_pelicans + print *, pelican_weight + print *, pelican_population_dynamics + print *, is_young_pelican + print *, pelican_tag + + pelican_tag = 'F' + +end program variables From eabc28b9a12a771264056cb2cf317b90c5181ba0 Mon Sep 17 00:00:00 2001 From: Dimitrios Theodorakis Date: Fri, 25 Apr 2025 14:01:56 +0100 Subject: [PATCH 10/26] Further split up the basics section - adds in the section on Maths --- config.yaml | 9 +- episodes/02-basics-logic.md | 26 + episodes/02-basics-maths.md | 568 ++++++++++++++++++ episodes/02-basics-strings.md | 26 + episodes/02-basics-variables.md | 25 +- .../02_maths/lennard_jones_potential.f90 | 35 ++ exercises/solutions/02_maths/maths.f90 | 28 + learners/discuss.md | 20 + 8 files changed, 729 insertions(+), 8 deletions(-) create mode 100644 episodes/02-basics-logic.md create mode 100644 episodes/02-basics-maths.md create mode 100644 episodes/02-basics-strings.md create mode 100644 exercises/solutions/02_maths/lennard_jones_potential.f90 create mode 100644 exercises/solutions/02_maths/maths.f90 create mode 100644 learners/discuss.md diff --git a/config.yaml b/config.yaml index ffedadd8..26ab05a2 100644 --- a/config.yaml +++ b/config.yaml @@ -60,8 +60,12 @@ contact: 'd.theodorakis@metoffice.gov.uk' # Order of episodes in your lesson episodes: -- introduction.Rmd -- 02-basics.Rmd +# - introduction.Rmd +# - 02-basics.Rmd +- 02-basics-variables.md +- 02-basics-maths.md +- 02-basics-logic.md +- 02-basics-strings.md # Information for Learners learners: @@ -79,4 +83,3 @@ profiles: sandpaper: astroDimitrios/sandpaper pegboard: astroDimitrios/pegboard varnish: astroDimitrios/varnish - diff --git a/episodes/02-basics-logic.md b/episodes/02-basics-logic.md new file mode 100644 index 00000000..128fbebf --- /dev/null +++ b/episodes/02-basics-logic.md @@ -0,0 +1,26 @@ +--- +title: 'Logic' +teaching: 15 +exercises: 15 +--- + +::::::::::::::::::::::::::::::::::::: questions + +- How do we declare and assign values to variables? + +:::::::::::::::::::::::::::::::::::::::::::::::: + +::::::::::::::::::::::::::::::::::::: objectives + +- Understand the different intrinsic data types. +- Declare and assign variables and parameters. + +:::::::::::::::::::::::::::::::::::::::::::::::: + +Variables store information we can use in our + +:::::::::::::::::::::::::::::::::::::::: keypoints + +- Th + +:::::::::::::::::::::::::::::::::::::::::::::::::: diff --git a/episodes/02-basics-maths.md b/episodes/02-basics-maths.md new file mode 100644 index 00000000..d81f4c0f --- /dev/null +++ b/episodes/02-basics-maths.md @@ -0,0 +1,568 @@ +--- +title: 'Maths' +teaching: 15 +exercises: 15 +--- + +::::::::::::::::::::::::::::::::::::: questions + +- How do we perform calculations in Fortran? + +:::::::::::::::::::::::::::::::::::::::::::::::: + +::::::::::::::::::::::::::::::::::::: objectives + +- Use the built in operators and intrinsic functions. +- Specify the precision of our numeric variables. + +:::::::::::::::::::::::::::::::::::::::::::::::: + +In this episode we will look at arithmetic with Fortran. +Arithmetic operations are where Fortran shines. +Fortran is much faster than Python, Matlab and +other interpreted languages. +Good Fortran code will be comparable in speed, +perhaps faster, than equivalent C/C++ code +depending on your compiler and optimisations. +To provide a good starting point for this episode +complete the challenge below. + +:::: challenge + +## Create a `maths` Fortran program + +1. Create a new Fortran program `maths.f90`. +2. Define a real parameter `pi`. +3. Print the value of `pi`. +4. Check your program compiles and runs. + +::: solution + +```fortran +program maths + !! Test program to demonstrate Fortran arithmetic + + implicit none + + real, parameter :: pi = 3.141592654 + !! Value of pi + + print *, 'Pi = ', pi + +end program maths + +``` + +Notice the print statement outputs the string +`'Pi = '` before printing the value of `pi` on the same line. + +::: group-tab + +### GFortran + +```bash +gfortran -o maths maths.f90 +``` + +### Intel + +```bash +ifx -o maths maths.f90 +``` + +### Flang + +```bash +flang -o maths maths.f90 +``` + +### Cray + +```bash +ftn -o maths maths.f90 +``` + +::: + +```bash +./maths +``` + +Example output: + +```output + 3.14159274 +``` + +This is slightly different to the value we coded. +We will discuss why in the +[`kinds` section](./02-basics-maths.md#kinds) of this episode. + +::: +:::: + +## Operators & Intrinsics + +The usual operators are available in Fortran: + +Operator | Description +---------|------------ +`**` | Exponent +`*` | Multiplication +`/` | Division +`+` | Addition +`-` | Subtraction + +They are listed in order of precedence. +Fortran also has a number of +[intrinsic maths functions](https://fortran-lang.org/learn/intrinsics/math/). + +:::: challenge + +## Calculate the area of a circle with radius 5 cm + +1. Add two new real variables for the `radius` and `area` to your program. +2. Print the value of `radius` and `area`. +3. Calculate the area of the circle using $\pi r^2$. +4. Check your program compiles and runs. + +::: solution + +```fortran +program maths + !! Test program to demonstrate Fortran arithmetic + + implicit none + + real, parameter :: pi = 3.141592654 + !! Value of pi + + real :: radius + !! Radius of the circle in cm + real :: area + !! Area of the circle in cm + + radius = 5.0 ! cm + area = pi * radius**2 + + print *, 'Pi = ', pi + print *, 'Radius = ', radius, ' cm' + print *, 'Area = ', area, ' cm^2' + +end program maths + +``` + +::: group-tab + +### GFortran + +```bash +gfortran -o maths maths.f90 +``` + +### Intel + +```bash +ifx -o maths maths.f90 +``` + +### Flang + +```bash +flang -o maths maths.f90 +``` + +### Cray + +```bash +ftn -o maths maths.f90 +``` + +::: + +```bash +./maths +``` + +Example output: + +```output + Pi = 3.14159274 + Radius = 5.00000000 cm + Area = 78.5398178 cm^2 +``` + +::: +:::: + +## Kinds + +Numeric types such as `integer`, `real`, +and `complex` can have different floating-point precisions. +This is commonly 32 or 64-bit precision. +We can specify the precision using **kind** parameters. +There are two common ways to specify **kind** parameters: + +::: tab + +### Using `iso_fortran_env` + +We will be using `iso_fortran_env` throughout this lesson. +`iso_fortran_env` is an intrinsic Fortran module. +It was introduced in the F2008 standard. + +```fortran +! This goes after the program statement +! We will cover modules in a later episode +use, intrinsic :: iso_fortran_env, only: r_32 => real32, r_64 => real64 +``` + +These parameters are then used when declaring variables: + +```fortran +real(kind=r_32), parameter :: earth_radius = 6371_r_32 ! km - single precision +real(kind=r_64) :: current_distance_from_sun ! Au - double precision +``` + +and when assigning values to variables: + +```fortran +current_distance_from_sun = 1.3_r_64 +``` + +### Using intrinsic functions + +```fortran +! hardware specific 32 bit real +integer, parameter :: r_32 = selected_real_kind(6, 37) +! hardware specific 64 bit real +integer, parameter :: r_64 = selected_real_kind(15, 307) +``` + +These parameters are then used when declaring variables: + +```fortran +real(kind=r_32), parameter :: earth_radius = 6371_r_32 ! km +real(kind=r_64) :: current_distance_from_sun ! Au +``` + +and when assigning values to variables: + +```fortran +current_distance_from_sun = 1.3_r_64 +``` + +::: + +::: caution + +### Fortran defaults to single precision + +Fortran differs from other languages. +The default precision for reals is single. + +```fortran +use, intrinsic :: iso_fortran_env, only: r_64 => real64 + +real(kind=r_64) :: current_distance_from_sun ! Au + +current_distance_from_sun = 1.3 ! no kind suffix - this is single precision +current_distance_from_sun = 1.3_r_64 ! double precision +``` + +**Always** use a kind suffix for real and integer types. + +::: + +::: spoiler + +### Style + +You can omit the `kind=`: + +```fortran +real(kind=r_64) +! is the same as +real(r_64) +``` + +In this lesson we prefer explicitly stating `kind=`. + +::: + +::: spoiler + +### Legacy + +You might see variables declared as: + +```fortran +15d00 ! 15.00 +9.375d-11 ! 9.375E-11 +``` + +The `d` here specifies the reals as double precision. + +You might also see variables with kinds which are plain integers: + +```fortran +real(8) +``` + +Some information on this older style is available in the +[gfortran docs](https://gcc.gnu.org/onlinedocs/gfortran/KIND-Type-Parameters.html). + +::: + +:::: challenge + +## Specify the precision in your program + +Update your `maths.f90` program to specify the real kind as `real64`. +Note the output **before** and **after** you modify the precision. +How has the output changed? + +::: solution + +```fortran +program maths + !! Test program to demonstrate Fortran arithmetic + + use, intrinsic :: iso_fortran_env, only: r_64 => real64 + + implicit none + + real(kind=r_64), parameter :: pi = 3.141592654_r_64 + !! Value of pi + + real(kind=r_64) :: radius + !! Radius of the circle in cm + real(kind=r_64) :: area + !! Area of the circle in cm + + ! this float must be written as 5.0 (sometimes seen as 5.) + ! not 5 on its own without the decimal point + radius = 5.0_r_64 ! cm + area = pi * radius**2.0_r_64 + + print *, 'Pi = ', pi + print *, 'Radius = ', radius, ' cm' + print *, 'Area = ', area, ' cm^2' + +end program maths + +``` + +Example output before (32 bit single precision): + +```output +Pi = 3.14159274 +Radius = 5.00000000 cm +Area = 78.5398178 cm^2 +``` + +Example output after (64 bit double precision): + +```output +Pi = 3.1415926540000001 +Radius = 5.0000000000000000 cm +Area = 78.539816349999995 cm^2 +``` + +The value of pi now accurately reflects the value you coded in the program. +The value of the area is also now more accurate. + +::: +:::: + +:::: challenge + +## Lennard-Jones Potential + +Create a new program to calculate the +[Lennard-Jones Potential](https://chem.libretexts.org/Bookshelves/Physical_and_Theoretical_Chemistry_Textbook_Maps/Supplemental_Modules_(Physical_and_Theoretical_Chemistry)/Physical_Properties_of_Matter/Atomic_and_Molecular_Properties/Intermolecular_Forces/Specific_Interactions/Lennard-Jones_Potential) +for two Xenon atoms. +They are separated by 4.0 Angstroms. +Approximate the potential using: + +$$V(r)=4\epsilon\left[\left(\frac{\sigma}{r}\right)^{12}-\left(\frac{\sigma}{r}\right)^{6}\right]$$ + +where: + +- $\epsilon=0.997\ kJ/mol$ +- $\sigma=3.40\ Angstroms$ +- $r=4.0\ Angstroms$ + +Print the value at the end of your program. + +::: solution + +In the file `lennard_jones_potential.f90`: + +```fortran +program lennard_jones_potential + !! Calculates the Lennard-Jones Potential for 2 Xenon atoms + + use, intrinsic :: iso_fortran_env, only: i_64 => int64, r_64 => real64 + + implicit none + + real(kind=r_64), parameter :: epsilon = 0.997_r_64 ! kJ/mol + !! well depth kJ/mol + real(kind=r_64), parameter :: sigma = 3.40_r_64 ! Angstroms + !! van der Waals radius Angstroms + integer(kind=i_64), parameter :: lj_potential_const = 4_i_64 + !! unit-less Lennard-Jones Potential constant + + real(kind=r_64) :: separation_distance + !! separation distance r in Angstroms + real(kind=r_64) :: lj_potential + !! Lennard-Jones Potential kJ/mol + + separation_distance = 4.0_r_64 ! Angstroms + + ! Calculate the Lennard-Jones Potential using: + ! V(r) = 4*epsilon*[(sigma/r)**12 - (sigma/r)**6] + lj_potential = lj_potential_const * epsilon * & + ((sigma/separation_distance)**12 & + - (sigma/separation_distance)**6) + + print *, 'V(4.0 Angstrom) = ', lj_potential, ' kJ/mol' + +end program lennard_jones_potential + +``` + +Note: + +- We couldn't name the `lj_potential` variable `lennard_jones_potential` + since that's the program name. + Try changing it to be the same as the program name. + What compiler error do you get? +- We've used verbose names for all variables. + This is good practice since using single character maths related + variable names makes code harder to read. + This does mean that the formula on one line + would break the 80 character line limit in our style guide. + To get around this the formula has been split over multiple lines + using the continuation marker `&`. +- The constant `lj_potential_const` has been defined as a variable. + This avoids [magic numbers](https://en.wikipedia.org/wiki/Magic_number_(programming)) in our code. + +::: +:::: + +### Type Casting & Mixed Precision + +Take a look at the calculation of `lj_potential` (from the challenge above). +What kinds are each of the variables in the equation? + +```fortran +lj_potential = lj_potential_const * epsilon * & + ((sigma/separation_distance)**12 & + - (sigma/separation_distance)**6) +``` + +The value we are calculating is a 64 bit real (`lj_potential`). +The first variable in the equation is a 64 bit integer (`lj_potential_const`). +The compiler will ***cast*** the integer +to a 64 bit real before using it in the multiplication. + +In this program the casting only occurs once. +Doing this casting many times can slow a program down. +To remove this implicit casting we can: + +- cast `lj_potential_const` to a real explicitly before + the calculation. + + ```fortran + lj_potential_const_real = real(lj_potential_const, r_64) + ``` + + We would also have to define the new real variable `lj_potential_const_real`. +- declare the constant as a real to start with. + This is the simplest solution which avoids casting. + + ```fortran + real(kind=r_64), parameter :: lj_potential_const = 4.0_r_64 + ``` + +This program uses mixed kinds in its arithmetic. +Some programs have mixed precision as well. +i.e. 32 bit integers in an equation with 64 bit integers. +In this case the 32 bit integers are promoted to 64 bit +by the compiler before use. +It is best to avoid implicit conversions like this +by being consistent with your precision. + +:::: challenge + +1. Find your compiler documentation. + Is there a flag that warns you about implicit conversions / kind casting? + If there is, compile your program with the flag. + What warning do you get? +2. Modify your solution to the last challenge to remove + any kind casting. + +::: solution + +1. For gfortran there are two flags we can use. + These are taken from the + [gfortran options documentation](https://gcc.gnu.org/onlinedocs/gfortran/Error-and-Warning-Options.html): + + ```shell + $ gfortran -o ljp lennard_jones_potential.f90 -Wconversion -Wconversion-extra + ``` + +2. Example modified code with no kind casting: + +```fortran +program lennard_jones_potential + !! Calculates the Lennard-Jones Potential for 2 Xenon atoms + + use, intrinsic :: iso_fortran_env, only: r_64 => real64 + + implicit none + + real(kind=r_64), parameter :: epsilon = 0.997_r_64 ! kJ/mol + !! well depth kJ/mol + real(kind=r_64), parameter :: sigma = 3.40_r_64 ! Angstroms + !! van der Waals radius Angstroms + real(kind=r_64), parameter :: lj_potential_const = 4_r_64 + !! unit-less Lennard-Jones Potential constant + + real(kind=r_64) :: separation_distance + !! separation distance r in Angstroms + real(kind=r_64) :: lj_potential + !! Lennard-Jones Potential kJ/mol + + separation_distance = 4.0_r_64 ! Angstroms + + ! Calculate the Lennard-Jones Potential using: + ! V(r) = 4*epsilon*[(sigma/r)**12 - (sigma/r)**6] + lj_potential = lj_potential_const * epsilon * & + ((sigma/separation_distance)**12 & + - (sigma/separation_distance)**6) + + print *, 'V(4.0 Angstrom) = ', lj_potential, ' kJ/mol' + +end program lennard_jones_potential + +``` + +The exponents in the equation `12` and `6` +have been left as integers. +They have no kind suffix. +This means they are the compilers default integer kind. +Using an integer exponent where possible +is faster than using a real exponent. + +::: +:::: + +:::::::::::::::::::::::::::::::::::::::: keypoints + +- Th + +:::::::::::::::::::::::::::::::::::::::::::::::::: diff --git a/episodes/02-basics-strings.md b/episodes/02-basics-strings.md new file mode 100644 index 00000000..4f73e9d4 --- /dev/null +++ b/episodes/02-basics-strings.md @@ -0,0 +1,26 @@ +--- +title: 'Strings' +teaching: 15 +exercises: 15 +--- + +::::::::::::::::::::::::::::::::::::: questions + +- How do we declare and assign values to variables? + +:::::::::::::::::::::::::::::::::::::::::::::::: + +::::::::::::::::::::::::::::::::::::: objectives + +- Understand the different intrinsic data types. +- Declare and assign variables and parameters. + +:::::::::::::::::::::::::::::::::::::::::::::::: + +Variables store information we can use in our + +:::::::::::::::::::::::::::::::::::::::: keypoints + +- Th + +:::::::::::::::::::::::::::::::::::::::::::::::::: diff --git a/episodes/02-basics-variables.md b/episodes/02-basics-variables.md index 2c19af55..71576c73 100644 --- a/episodes/02-basics-variables.md +++ b/episodes/02-basics-variables.md @@ -22,11 +22,13 @@ Fortran is a ***strongly typed*** language. Each variable must be declared with a type.[^static] Fortran provides the following intrinsic (built-in) data types for variables: -- `integer` -- `real` -- `complex` -- `logical` -- `character` +- `integer`: A whole number which is positive, negative or zero. +- `real`: A real number includes the fractional part, + even if the fractional part is 0. +- `complex`: A number made up of a real and an imaginary part. +- `logical`: A boolean value which can be **.true.** or **.false.**. +- `character`: A single ASCII character. + Strings are made from sequences of characters. Variable names must be 63 characters or less (F2003 standard). No spaces are allowed. @@ -36,6 +38,11 @@ Names must begin with a letter. This also applies to names for programs, modules, subroutines, and functions, which you will learn about later in the course. +Do **not** use Fortran keywords as variable names. +Fortran will let you overwrite these keywords if you're not careful. +The [Fortran wiki keywords page](https://fortranwiki.org/fortran/show/Keywords) +contains a list of all Fortran keywords. + [^static]: Fortran is also ***statically typed***. You can not change a variables type after the variable declaration. @@ -55,6 +62,8 @@ integer :: number_of_pelicans :::: challenge +## Create a `variables` program + 1. Create a new Fortran program named `variables.f90`. 2. Declare 5 variables. One of each of the 5 intrinsic data types. 3. Print your variables. @@ -177,6 +186,8 @@ by single or double quotes. :::: challenge +## Modify your `variables` program + 1. Assign values to the variables in your program. 2. Compile and run your program. @@ -333,6 +344,8 @@ character, parameter :: pelican_tag = 'J' :::: challenge +## Add a parameter to your `variables` program + 1. Modify a variable in your code to be a parameter. 2. Try modifying the parameter in your code. What output do you get when compiling? @@ -398,6 +411,8 @@ This will aid your debugging and help make your code more portable. :::: challenge +## Tidy up your program + 1. Make sure your code conforms to the style followed by this course. 2. Add Ford comments to document the program diff --git a/exercises/solutions/02_maths/lennard_jones_potential.f90 b/exercises/solutions/02_maths/lennard_jones_potential.f90 new file mode 100644 index 00000000..e59ddfc3 --- /dev/null +++ b/exercises/solutions/02_maths/lennard_jones_potential.f90 @@ -0,0 +1,35 @@ +!-------------------------------------------------------- +! Carpentries - Introduction to Modern Fortran +! CC BY 4.0, https://creativecommons.org/licenses/by/4.0/ +!-------------------------------------------------------- +! +program lennard_jones_potential + !! Calculates the Lennard-Jones Potential for 2 Xenon atoms + + use, intrinsic :: iso_fortran_env, only: i_64 => int64, r_64 => real64 + + implicit none + + real(kind=r_64), parameter :: epsilon = 0.997_r_64 ! kJ/mol + !! well depth kJ/mol + real(kind=r_64), parameter :: sigma = 3.40_r_64 ! Angstroms + !! van der Waals radius Angstroms + integer(kind=i_64) :: lj_potential_const = 4_i_64 + !! unit-less Lennard-Jones Potential constant + + real(kind=r_64) :: separation_distance + !! separation distance r in Angstroms + real(kind=r_64) :: lj_potential + !! Lennard-Jones Potential kJ/mol + + separation_distance = 4.0_r_64 ! Angstroms + + ! Calculate the Lennard-Jones Potential using: + ! V(r) = 4*epsilon*[(sigma/r)**12 - (sigma/r)**6] + lj_potential = lj_potential_const * epsilon * & + ((sigma/separation_distance)**12 & + - (sigma/separation_distance)**6) + + print *, 'V(4.0 Angstrom) = ', lj_potential, ' kJ/mol' + +end program lennard_jones_potential diff --git a/exercises/solutions/02_maths/maths.f90 b/exercises/solutions/02_maths/maths.f90 new file mode 100644 index 00000000..443642f5 --- /dev/null +++ b/exercises/solutions/02_maths/maths.f90 @@ -0,0 +1,28 @@ +!-------------------------------------------------------- +! Carpentries - Introduction to Modern Fortran +! CC BY 4.0, https://creativecommons.org/licenses/by/4.0/ +!-------------------------------------------------------- +! +program maths + !! Test program to demonstrate Fortran arithmetic + + use, intrinsic :: iso_fortran_env, only: r_64 => real64 + + implicit none + + real(kind=r_64), parameter :: pi = 3.141592654_r_64 + !! Value of pi + + real(kind=r_64) :: radius + !! Radius of the circle in cm + real(kind=r_64) :: area + !! Area of the circle in cm + + radius = 5.0_r_64 ! cm + area = pi * radius**2.0_r_64 + + print *, 'Pi = ', pi + print *, 'Radius = ', radius, ' cm' + print *, 'Area = ', area, ' cm^2' + +end program maths diff --git a/learners/discuss.md b/learners/discuss.md new file mode 100644 index 00000000..3f0a8aba --- /dev/null +++ b/learners/discuss.md @@ -0,0 +1,20 @@ +--- +title: Discussion +--- + +## Frequently Asked Questions + +People often have questions about Fortran beyond the scope of the core material. +Students who have completed episodes early +might find value in looking through the following topics. + +Note that since this material isn't essential for most Fortran usage, +it won't be covered by the instructor. + +## Floating point precision + +There is a great article +[Examples of floating point problems](https://jvns.ca/blog/2023/01/13/examples-of-floating-point-problems/) +by Julia Evans. +The article outlines what floating points numbers are +and common problems with precision. From 2e2cf0b486cdf00905c2a532037cf7eab315d071 Mon Sep 17 00:00:00 2001 From: Dimitrios Theodorakis Date: Fri, 25 Apr 2025 15:12:01 +0100 Subject: [PATCH 11/26] Remove whitespace --- exercises/solutions/02_maths/lennard_jones_potential.f90 | 2 +- exercises/solutions/02_maths/maths.f90 | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/exercises/solutions/02_maths/lennard_jones_potential.f90 b/exercises/solutions/02_maths/lennard_jones_potential.f90 index e59ddfc3..555a0151 100644 --- a/exercises/solutions/02_maths/lennard_jones_potential.f90 +++ b/exercises/solutions/02_maths/lennard_jones_potential.f90 @@ -16,7 +16,7 @@ program lennard_jones_potential !! van der Waals radius Angstroms integer(kind=i_64) :: lj_potential_const = 4_i_64 !! unit-less Lennard-Jones Potential constant - + real(kind=r_64) :: separation_distance !! separation distance r in Angstroms real(kind=r_64) :: lj_potential diff --git a/exercises/solutions/02_maths/maths.f90 b/exercises/solutions/02_maths/maths.f90 index 443642f5..14169c6b 100644 --- a/exercises/solutions/02_maths/maths.f90 +++ b/exercises/solutions/02_maths/maths.f90 @@ -12,14 +12,14 @@ program maths real(kind=r_64), parameter :: pi = 3.141592654_r_64 !! Value of pi - + real(kind=r_64) :: radius !! Radius of the circle in cm real(kind=r_64) :: area !! Area of the circle in cm radius = 5.0_r_64 ! cm - area = pi * radius**2.0_r_64 + area = pi * radius**2 print *, 'Pi = ', pi print *, 'Radius = ', radius, ' cm' From 80b70063b14c24169ecceb0f6db59cc45f5ea27d Mon Sep 17 00:00:00 2001 From: Dimitrios Theodorakis Date: Fri, 25 Apr 2025 15:12:14 +0100 Subject: [PATCH 12/26] Line highlighting --- episodes/02-basics-maths.md | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/episodes/02-basics-maths.md b/episodes/02-basics-maths.md index d81f4c0f..6ce50a0b 100644 --- a/episodes/02-basics-maths.md +++ b/episodes/02-basics-maths.md @@ -325,26 +325,27 @@ How has the output changed? ::: solution -```fortran +
+
 program maths
     !! Test program to demonstrate Fortran arithmetic
 
-    use, intrinsic :: iso_fortran_env, only: r_64 => real64
+    use, intrinsic :: iso_fortran_env, only: r_64 => real64
 
     implicit none
 
-    real(kind=r_64), parameter :: pi = 3.141592654_r_64
+    real(kind=r_64), parameter :: pi = 3.141592654_r_64
       !! Value of pi
-    
-    real(kind=r_64) :: radius
+
+    real(kind=r_64) :: radius
       !! Radius of the circle in cm
-    real(kind=r_64) :: area
+    real(kind=r_64) :: area
       !! Area of the circle in cm
 
     ! this float must be written as 5.0 (sometimes seen as 5.)
     ! not 5 on its own without the decimal point
     radius = 5.0_r_64  ! cm
-    area = pi * radius**2.0_r_64
+    area = pi * radius**2
 
     print *, 'Pi = ', pi
     print *, 'Radius = ', radius, ' cm'
@@ -352,7 +353,8 @@ program maths
 
 end program maths
 
-```
+
+
Example output before (32 bit single precision): @@ -517,11 +519,12 @@ by being consistent with your precision. 2. Example modified code with no kind casting: -```fortran +
+
 program lennard_jones_potential
     !! Calculates the Lennard-Jones Potential for 2 Xenon atoms
 
-    use, intrinsic :: iso_fortran_env, only: r_64 => real64
+    use, intrinsic :: iso_fortran_env, only: r_64 => real64
 
     implicit none
 
@@ -529,9 +532,9 @@ program lennard_jones_potential
       !! well depth kJ/mol
     real(kind=r_64), parameter :: sigma = 3.40_r_64     ! Angstroms
       !! van der Waals radius Angstroms
-    real(kind=r_64), parameter :: lj_potential_const = 4_r_64
+    real(kind=r_64), parameter :: lj_potential_const = 4_r_64
       !! unit-less Lennard-Jones Potential constant
-    
+
     real(kind=r_64) :: separation_distance
       !! separation distance r in Angstroms
     real(kind=r_64) :: lj_potential
@@ -549,7 +552,8 @@ program lennard_jones_potential
 
 end program lennard_jones_potential
 
-```
+
+
The exponents in the equation `12` and `6` have been left as integers. From c2a2b9df5349717c0c92449e55e4be1e11a512d7 Mon Sep 17 00:00:00 2001 From: Dimitrios Theodorakis Date: Fri, 25 Apr 2025 15:12:22 +0100 Subject: [PATCH 13/26] Ignore executables --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 8b152582..56317407 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,8 @@ renv/sandbox # OPTIONAL # Editors .vscode + +# Executables +a.out +exercises/*/solutions/* +!exercises/*/solutions/*.* From 43efde7689fe0ae9a98732486d3e2af9402568ed Mon Sep 17 00:00:00 2001 From: Dimitrios Theodorakis Date: Fri, 25 Apr 2025 15:14:14 +0100 Subject: [PATCH 14/26] Alter the exercises directory structure --- exercises/02_maths/README.md | 0 .../02_maths => 02_maths/solutions}/lennard_jones_potential.f90 | 0 exercises/{solutions/02_maths => 02_maths/solutions}/maths.f90 | 0 exercises/02_variables/README.md | 0 .../02_variables => 02_variables/solutions}/variables.f90 | 0 5 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 exercises/02_maths/README.md rename exercises/{solutions/02_maths => 02_maths/solutions}/lennard_jones_potential.f90 (100%) rename exercises/{solutions/02_maths => 02_maths/solutions}/maths.f90 (100%) create mode 100644 exercises/02_variables/README.md rename exercises/{solutions/02_variables => 02_variables/solutions}/variables.f90 (100%) diff --git a/exercises/02_maths/README.md b/exercises/02_maths/README.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/solutions/02_maths/lennard_jones_potential.f90 b/exercises/02_maths/solutions/lennard_jones_potential.f90 similarity index 100% rename from exercises/solutions/02_maths/lennard_jones_potential.f90 rename to exercises/02_maths/solutions/lennard_jones_potential.f90 diff --git a/exercises/solutions/02_maths/maths.f90 b/exercises/02_maths/solutions/maths.f90 similarity index 100% rename from exercises/solutions/02_maths/maths.f90 rename to exercises/02_maths/solutions/maths.f90 diff --git a/exercises/02_variables/README.md b/exercises/02_variables/README.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/solutions/02_variables/variables.f90 b/exercises/02_variables/solutions/variables.f90 similarity index 100% rename from exercises/solutions/02_variables/variables.f90 rename to exercises/02_variables/solutions/variables.f90 From cb8301e45ec13fb73aa7f5d3c1d6f422eed71dc8 Mon Sep 17 00:00:00 2001 From: Dimitrios Theodorakis Date: Fri, 25 Apr 2025 16:00:12 +0100 Subject: [PATCH 15/26] Tidy variables.f90 code --- .../02_variables/solutions/variables.f90 | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/exercises/02_variables/solutions/variables.f90 b/exercises/02_variables/solutions/variables.f90 index 783c5dae..5259b7e8 100644 --- a/exercises/02_variables/solutions/variables.f90 +++ b/exercises/02_variables/solutions/variables.f90 @@ -4,20 +4,27 @@ !-------------------------------------------------------- ! program variables + !! A test program to lean how to declare and assign variables. implicit none - integer :: number_of_pelicans - real :: pelican_weight - complex :: pelican_population_dynamics - logical :: is_young_pelican + integer :: number_of_pelicans + !! The number of pelicans in the pod + real :: pelican_weight + !! The average weight of a pelican in the pod / kg + complex :: pelican_population_dynamics + !! The birth and death rate as a complex number + !! Units are the number of pelicans per year + logical :: is_young_pelican + !! Test to see if the current pelican is young + character, parameter :: pelican_tag = 'J' + !! Pelican pod tracking tag number_of_pelicans = 5 - pelican_weight = 2.5 ! kg - pelican_population_dynamics = (-1.2, 0.9) + pelican_weight = 2.5 ! kg + pelican_population_dynamics = (-1.2, 0.9) ! births, deaths per year is_young_pelican = .false. - ! pelican_tag = 'J' print *, number_of_pelicans print *, pelican_weight @@ -25,6 +32,4 @@ program variables print *, is_young_pelican print *, pelican_tag - pelican_tag = 'F' - end program variables From 67ae508d00124bff9ab85eb9cab3a8eb81020d9b Mon Sep 17 00:00:00 2001 From: Dimitrios Theodorakis Date: Fri, 25 Apr 2025 16:00:33 +0100 Subject: [PATCH 16/26] Add challenges to the exercises directory for the variables section --- exercises/02_variables/README.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/exercises/02_variables/README.md b/exercises/02_variables/README.md index e69de29b..539d10c6 100644 --- a/exercises/02_variables/README.md +++ b/exercises/02_variables/README.md @@ -0,0 +1,28 @@ +# Challenges + +## Create a `variables` program + +1. Create a new Fortran program named `variables.f90`. +2. Declare 5 variables. One of each of the 5 intrinsic data types. +3. Print your variables. +4. Compile and run your program. + +What do you notice about the output? + +## Modify your `variables` program + +1. Assign values to the variables in your program. +2. Compile and run your program. + +## Add a parameter to your `variables` program + +1. Modify a variable in your code to be a parameter. +2. Try modifying the parameter in your code. + What output do you get when compiling? + +## Tidy up your program + +1. Make sure your code conforms to the style +followed by this course. +2. Add Ford comments to document the program +and each variable. From e92d2386bf216dad2ce28a212a3871318198759e Mon Sep 17 00:00:00 2001 From: Dimitrios Theodorakis Date: Fri, 25 Apr 2025 16:00:53 +0100 Subject: [PATCH 17/26] Add challenges to the README for the basic maths section - make sure all challenges have names --- episodes/02-basics-maths.md | 2 ++ exercises/02_maths/README.md | 48 ++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/episodes/02-basics-maths.md b/episodes/02-basics-maths.md index 6ce50a0b..81c98850 100644 --- a/episodes/02-basics-maths.md +++ b/episodes/02-basics-maths.md @@ -500,6 +500,8 @@ by being consistent with your precision. :::: challenge +## Remove Casting + 1. Find your compiler documentation. Is there a flag that warns you about implicit conversions / kind casting? If there is, compile your program with the flag. diff --git a/exercises/02_maths/README.md b/exercises/02_maths/README.md index e69de29b..84a91f40 100644 --- a/exercises/02_maths/README.md +++ b/exercises/02_maths/README.md @@ -0,0 +1,48 @@ +# Challenges + +## Create a `maths` Fortran program + +1. Create a new Fortran program `maths.f90`. +2. Define a real parameter `pi`. +3. Print the value of `pi`. +4. Check your program compiles and runs. + +## Calculate the area of a circle with radius 5 cm + +1. Add two new real variables for the `radius` and `area` to your program. +2. Print the value of `radius` and `area`. +3. Calculate the area of the circle using $\pi r^2$. +4. Check your program compiles and runs. + +## Specify the precision in your program + +Update your `maths.f90` program to specify the real kind as `real64`. +Note the output **before** and **after** you modify the precision. +How has the output changed? + +## Lennard-Jones Potential + +Create a new program to calculate the +[Lennard-Jones Potential](https://chem.libretexts.org/Bookshelves/Physical_and_Theoretical_Chemistry_Textbook_Maps/Supplemental_Modules_(Physical_and_Theoretical_Chemistry)/Physical_Properties_of_Matter/Atomic_and_Molecular_Properties/Intermolecular_Forces/Specific_Interactions/Lennard-Jones_Potential) +for two Xenon atoms. +They are separated by 4.0 Angstroms. +Approximate the potential using: + +$$V(r)=4\epsilon\left[\left(\frac{\sigma}{r}\right)^{12}-\left(\frac{\sigma}{r}\right)^{6}\right]$$ + +where: + +- $\epsilon=0.997\ kJ/mol$ +- $\sigma=3.40\ Angstroms$ +- $r=4.0\ Angstroms$ + +Print the value at the end of your program. + +## Remove Casting + +1. Find your compiler documentation. + Is there a flag that warns you about implicit conversions / kind casting? + If there is, compile your program with the flag. + What warning do you get? +2. Modify your solution to the last challenge to remove + any kind casting. From 0539a7f7daaca1fe64877e6c3cc8a3fcab39c6cf Mon Sep 17 00:00:00 2001 From: Dimitrios Theodorakis Date: Fri, 25 Apr 2025 16:04:18 +0100 Subject: [PATCH 18/26] Undo line highlighting attempt --- episodes/02-basics-maths.md | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/episodes/02-basics-maths.md b/episodes/02-basics-maths.md index 81c98850..303888f4 100644 --- a/episodes/02-basics-maths.md +++ b/episodes/02-basics-maths.md @@ -325,21 +325,20 @@ How has the output changed? ::: solution -
-
+```fortran
 program maths
     !! Test program to demonstrate Fortran arithmetic
 
-    use, intrinsic :: iso_fortran_env, only: r_64 => real64
+    use, intrinsic :: iso_fortran_env, only: r_64 => real64
 
     implicit none
 
-    real(kind=r_64), parameter :: pi = 3.141592654_r_64
+    real(kind=r_64), parameter :: pi = 3.141592654_r_64
       !! Value of pi
 
-    real(kind=r_64) :: radius
+    real(kind=r_64) :: radius
       !! Radius of the circle in cm
-    real(kind=r_64) :: area
+    real(kind=r_64) :: area
       !! Area of the circle in cm
 
     ! this float must be written as 5.0 (sometimes seen as 5.)
@@ -353,8 +352,7 @@ program maths
 
 end program maths
 
-
-
+``` Example output before (32 bit single precision): @@ -521,12 +519,11 @@ by being consistent with your precision. 2. Example modified code with no kind casting: -
-
+```fortran
 program lennard_jones_potential
     !! Calculates the Lennard-Jones Potential for 2 Xenon atoms
 
-    use, intrinsic :: iso_fortran_env, only: r_64 => real64
+    use, intrinsic :: iso_fortran_env, only: r_64 => real64
 
     implicit none
 
@@ -534,7 +531,7 @@ program lennard_jones_potential
       !! well depth kJ/mol
     real(kind=r_64), parameter :: sigma = 3.40_r_64     ! Angstroms
       !! van der Waals radius Angstroms
-    real(kind=r_64), parameter :: lj_potential_const = 4_r_64
+    real(kind=r_64), parameter :: lj_potential_const = 4_r_64
       !! unit-less Lennard-Jones Potential constant
 
     real(kind=r_64) :: separation_distance
@@ -554,8 +551,7 @@ program lennard_jones_potential
 
 end program lennard_jones_potential
 
-
-
+``` The exponents in the equation `12` and `6` have been left as integers. From a1420068503b6a03741adf9b403cea54cbc2ba88 Mon Sep 17 00:00:00 2001 From: Dimitrios Theodorakis Date: Fri, 25 Apr 2025 16:13:25 +0100 Subject: [PATCH 19/26] Undo compiler flag testing with ljp code --- exercises/02_maths/solutions/lennard_jones_potential.f90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/exercises/02_maths/solutions/lennard_jones_potential.f90 b/exercises/02_maths/solutions/lennard_jones_potential.f90 index 555a0151..de958743 100644 --- a/exercises/02_maths/solutions/lennard_jones_potential.f90 +++ b/exercises/02_maths/solutions/lennard_jones_potential.f90 @@ -6,15 +6,15 @@ program lennard_jones_potential !! Calculates the Lennard-Jones Potential for 2 Xenon atoms - use, intrinsic :: iso_fortran_env, only: i_64 => int64, r_64 => real64 + use, intrinsic :: iso_fortran_env, only: r_64 => real64 implicit none - real(kind=r_64), parameter :: epsilon = 0.997_r_64 ! kJ/mol + real(kind=r_64), parameter :: epsilon = 0.997_r_64 ! kJ/mol !! well depth kJ/mol - real(kind=r_64), parameter :: sigma = 3.40_r_64 ! Angstroms + real(kind=r_64), parameter :: sigma = 3.40_r_64 ! Angstroms !! van der Waals radius Angstroms - integer(kind=i_64) :: lj_potential_const = 4_i_64 + real(kind=r_64), parameter :: lj_potential_const = 4.0_r_64 !! unit-less Lennard-Jones Potential constant real(kind=r_64) :: separation_distance From 92d06853bb55a6800680cf3a88d3edf87fbaba2e Mon Sep 17 00:00:00 2001 From: Dimitrios Theodorakis Date: Fri, 25 Apr 2025 16:28:37 +0100 Subject: [PATCH 20/26] Add key points to the maths section --- episodes/02-basics-maths.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/episodes/02-basics-maths.md b/episodes/02-basics-maths.md index 303888f4..6bac592a 100644 --- a/episodes/02-basics-maths.md +++ b/episodes/02-basics-maths.md @@ -565,6 +565,16 @@ is faster than using a real exponent. :::::::::::::::::::::::::::::::::::::::: keypoints -- Th +- Operators in order of precedance: `**`, `*`, `/`, `+`, and `-`. +- List of [intrinsic maths functions](https://fortran-lang.org/learn/intrinsics/math/). +- A numeric variables **kind** specifies its floating-point precision. + 32-bit, 64-bit etc. +- Always specify a kind when defining and assigning values to variables. + Otherwise Fortran will default to the compilers single precision. +- Avoid mixing precision and kinds + (e.g. integers with reals, or 32-bit with 64-bit). + The compiler will implicitly convert the lower precision value + to a higher precision value. + This can slow down your programs. :::::::::::::::::::::::::::::::::::::::::::::::::: From 89404438078eb5fab774001c2b26d96002e83986 Mon Sep 17 00:00:00 2001 From: Dimitrios Theodorakis Date: Fri, 25 Apr 2025 16:58:07 +0100 Subject: [PATCH 21/26] Add in messages for where challenges should be written and update the exercises dir with the correct sub dir names --- config.yaml | 11 +++++------ ...{02-basics-variables.md => 02-variables.md} | 6 ++++++ episodes/{02-basics-maths.md => 03-maths.md} | 6 ++++++ episodes/{02-basics-logic.md => 04-logic.md} | 0 .../{02-basics-strings.md => 05-strings.md} | 0 episodes/data/exercises.tar.gz | Bin 359 -> 5764 bytes .../{02_variables => 02-variables}/README.md | 0 .../solutions/variables.f90 | 0 exercises/{02_maths => 03-maths}/README.md | 0 .../solutions/lennard_jones_potential.f90 | 0 .../{02_maths => 03-maths}/solutions/maths.f90 | 0 11 files changed, 17 insertions(+), 6 deletions(-) rename episodes/{02-basics-variables.md => 02-variables.md} (99%) rename episodes/{02-basics-maths.md => 03-maths.md} (99%) rename episodes/{02-basics-logic.md => 04-logic.md} (100%) rename episodes/{02-basics-strings.md => 05-strings.md} (100%) rename exercises/{02_variables => 02-variables}/README.md (100%) rename exercises/{02_variables => 02-variables}/solutions/variables.f90 (100%) rename exercises/{02_maths => 03-maths}/README.md (100%) rename exercises/{02_maths => 03-maths}/solutions/lennard_jones_potential.f90 (100%) rename exercises/{02_maths => 03-maths}/solutions/maths.f90 (100%) diff --git a/config.yaml b/config.yaml index 26ab05a2..bcf512d6 100644 --- a/config.yaml +++ b/config.yaml @@ -60,12 +60,11 @@ contact: 'd.theodorakis@metoffice.gov.uk' # Order of episodes in your lesson episodes: -# - introduction.Rmd -# - 02-basics.Rmd -- 02-basics-variables.md -- 02-basics-maths.md -- 02-basics-logic.md -- 02-basics-strings.md +- 01-trivial-program.md +- 02-variables.md +- 03-maths.md +- 04-logic.md +- 05-strings.md # Information for Learners learners: diff --git a/episodes/02-basics-variables.md b/episodes/02-variables.md similarity index 99% rename from episodes/02-basics-variables.md rename to episodes/02-variables.md index 71576c73..ff1bbe10 100644 --- a/episodes/02-basics-variables.md +++ b/episodes/02-variables.md @@ -60,6 +60,12 @@ So to declare an integer: integer :: number_of_pelicans ``` +For this episode complete challenges in: + +```bash +cd ~/Desktop/intro-to-modern-fortran/02-variables +``` + :::: challenge ## Create a `variables` program diff --git a/episodes/02-basics-maths.md b/episodes/03-maths.md similarity index 99% rename from episodes/02-basics-maths.md rename to episodes/03-maths.md index 6bac592a..a20a1917 100644 --- a/episodes/02-basics-maths.md +++ b/episodes/03-maths.md @@ -27,6 +27,12 @@ depending on your compiler and optimisations. To provide a good starting point for this episode complete the challenge below. +For this episode complete challenges in: + +```bash +cd ~/Desktop/intro-to-modern-fortran/03-maths +``` + :::: challenge ## Create a `maths` Fortran program diff --git a/episodes/02-basics-logic.md b/episodes/04-logic.md similarity index 100% rename from episodes/02-basics-logic.md rename to episodes/04-logic.md diff --git a/episodes/02-basics-strings.md b/episodes/05-strings.md similarity index 100% rename from episodes/02-basics-strings.md rename to episodes/05-strings.md diff --git a/episodes/data/exercises.tar.gz b/episodes/data/exercises.tar.gz index 1c2b985d619fb234d0f614665e58e441d7ad7beb..427295d22315ca2c2b9bd7fad9ddd4a6bf6dc183 100644 GIT binary patch literal 5764 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 F0005_wJrbv diff --git a/exercises/02_variables/README.md b/exercises/02-variables/README.md similarity index 100% rename from exercises/02_variables/README.md rename to exercises/02-variables/README.md diff --git a/exercises/02_variables/solutions/variables.f90 b/exercises/02-variables/solutions/variables.f90 similarity index 100% rename from exercises/02_variables/solutions/variables.f90 rename to exercises/02-variables/solutions/variables.f90 diff --git a/exercises/02_maths/README.md b/exercises/03-maths/README.md similarity index 100% rename from exercises/02_maths/README.md rename to exercises/03-maths/README.md diff --git a/exercises/02_maths/solutions/lennard_jones_potential.f90 b/exercises/03-maths/solutions/lennard_jones_potential.f90 similarity index 100% rename from exercises/02_maths/solutions/lennard_jones_potential.f90 rename to exercises/03-maths/solutions/lennard_jones_potential.f90 diff --git a/exercises/02_maths/solutions/maths.f90 b/exercises/03-maths/solutions/maths.f90 similarity index 100% rename from exercises/02_maths/solutions/maths.f90 rename to exercises/03-maths/solutions/maths.f90 From 36c7a98424677e3073495e22704c4e1672254926 Mon Sep 17 00:00:00 2001 From: Dimitrios Theodorakis Date: Fri, 25 Apr 2025 17:07:52 +0100 Subject: [PATCH 22/26] Fix config --- config.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/config.yaml b/config.yaml index bcf512d6..f44e8c05 100644 --- a/config.yaml +++ b/config.yaml @@ -60,7 +60,6 @@ contact: 'd.theodorakis@metoffice.gov.uk' # Order of episodes in your lesson episodes: -- 01-trivial-program.md - 02-variables.md - 03-maths.md - 04-logic.md From e285d817ac045085befb0fddff2e8626662c6c78 Mon Sep 17 00:00:00 2001 From: Dimitrios Theodorakis Date: Tue, 29 Apr 2025 10:42:41 +0100 Subject: [PATCH 23/26] Clarify type/kind and add explanation of the gfortran warning (or lack of) for parameter kind conversion --- episodes/03-maths.md | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/episodes/03-maths.md b/episodes/03-maths.md index a20a1917..1d78b95b 100644 --- a/episodes/03-maths.md +++ b/episodes/03-maths.md @@ -494,8 +494,8 @@ To remove this implicit casting we can: real(kind=r_64), parameter :: lj_potential_const = 4.0_r_64 ``` -This program uses mixed kinds in its arithmetic. -Some programs have mixed precision as well. +This program uses mixed type in its arithmetic (reals and integers). +Some programs have mixed precision (kinds) as well. i.e. 32 bit integers in an equation with 64 bit integers. In this case the 32 bit integers are promoted to 64 bit by the compiler before use. @@ -511,7 +511,7 @@ by being consistent with your precision. If there is, compile your program with the flag. What warning do you get? 2. Modify your solution to the last challenge to remove - any kind casting. + any type/kind casting. ::: solution @@ -523,7 +523,29 @@ by being consistent with your precision. $ gfortran -o ljp lennard_jones_potential.f90 -Wconversion -Wconversion-extra ``` -2. Example modified code with no kind casting: + Note that in the solution code to the last challenge + gfortran will still **not** provide a warning. + The parameter was declared as an integer + and used in arithmetic with reals (mixed type). + So why is there no warning about conversion? + + Because the variable is a parameter the compiler is performing + optimisations under the hood that remove the type casting. + Remove the parameter keyword and the following warning appears: + + ```output + lennard_jones_potential.f90:24:35: + + 24 | lj_potential = lj_potential_const * epsilon * & + | 1 + Warning: Conversion from INTEGER(8) to REAL(8) at (1) [-Wconversion-extra] + ``` + + So turning on compiler warnings can be useful + but are no substitute for thorough code and science review. + A later episode goes deeper into compiler usage and debugging. + +2. Example modified code with no type/kind casting: ```fortran program lennard_jones_potential From da2c2dd9b04af298bf8653719c36cf555ae9b79b Mon Sep 17 00:00:00 2001 From: Dimitrios Theodorakis Date: Tue, 29 Apr 2025 12:34:22 +0100 Subject: [PATCH 24/26] Update AU unit --- episodes/03-maths.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/episodes/03-maths.md b/episodes/03-maths.md index 1d78b95b..594b51ec 100644 --- a/episodes/03-maths.md +++ b/episodes/03-maths.md @@ -227,14 +227,14 @@ use, intrinsic :: iso_fortran_env, only: r_32 => real32, r_64 => real64 These parameters are then used when declaring variables: ```fortran -real(kind=r_32), parameter :: earth_radius = 6371_r_32 ! km - single precision -real(kind=r_64) :: current_distance_from_sun ! Au - double precision +real(kind=r_32), parameter :: earth_radius = 6371_r_32 ! km - single precision +real(kind=r_64) :: current_distance_from_sun ! AU - double precision ``` and when assigning values to variables: ```fortran -current_distance_from_sun = 1.3_r_64 +current_distance_from_sun = 1.3_r_64 ! AU ``` ### Using intrinsic functions From f9385d8b7819aff3e3fb820fd7e79dbeff44734d Mon Sep 17 00:00:00 2001 From: Dimitrios Theodorakis Date: Tue, 29 Apr 2025 12:35:31 +0100 Subject: [PATCH 25/26] Update more units AU --- episodes/03-maths.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/episodes/03-maths.md b/episodes/03-maths.md index 594b51ec..8c95314c 100644 --- a/episodes/03-maths.md +++ b/episodes/03-maths.md @@ -250,7 +250,7 @@ These parameters are then used when declaring variables: ```fortran real(kind=r_32), parameter :: earth_radius = 6371_r_32 ! km -real(kind=r_64) :: current_distance_from_sun ! Au +real(kind=r_64) :: current_distance_from_sun ! AU ``` and when assigning values to variables: @@ -271,7 +271,7 @@ The default precision for reals is single. ```fortran use, intrinsic :: iso_fortran_env, only: r_64 => real64 -real(kind=r_64) :: current_distance_from_sun ! Au +real(kind=r_64) :: current_distance_from_sun ! AU current_distance_from_sun = 1.3 ! no kind suffix - this is single precision current_distance_from_sun = 1.3_r_64 ! double precision From 604db39970fac237b2e85c908a27356a408127c9 Mon Sep 17 00:00:00 2001 From: Dimitrios Theodorakis Date: Tue, 29 Apr 2025 14:28:32 +0100 Subject: [PATCH 26/26] Add example of scientific notation --- episodes/02-variables.md | 12 +-- episodes/04-logic.md | 77 +++++++++++++++++- episodes/data/exercises.tar.gz | Bin 5764 -> 5817 bytes .../02-variables/solutions/variables.f90 | 4 +- 4 files changed, 84 insertions(+), 9 deletions(-) diff --git a/episodes/02-variables.md b/episodes/02-variables.md index ff1bbe10..b46c3594 100644 --- a/episodes/02-variables.md +++ b/episodes/02-variables.md @@ -181,7 +181,7 @@ For example: ```fortran number_of_pelicans = 5 pelican_weight = 2.5 ! kg -pelican_population_dynamics = (-1.2, 0.9) +pelican_population_dynamics = (-1.2e3, 0.9e2) ! Scientific notation, -1,200 etc is_young_pelican = .false. pelican_tag = 'Jeff' ``` @@ -212,7 +212,7 @@ program variables number_of_pelicans = 5 pelican_weight = 2.5 ! kg - pelican_population_dynamics = (-1.2, 0.9) + pelican_population_dynamics = (-1.2e3, 0.9e2) is_young_pelican = .false. pelican_tag = 'J' @@ -315,7 +315,7 @@ program variables number_of_pelicans = 5 pelican_weight = 2.5 ! kg - pelican_population_dynamics = (-1.2, 0.9) + pelican_population_dynamics = (-1.2e3, 0.9e2) is_young_pelican = .false. pelican_tag = 'J' @@ -372,7 +372,7 @@ program variables number_of_pelicans = 5 pelican_weight = 2.5 ! kg - pelican_population_dynamics = (-1.2, 0.9) + pelican_population_dynamics = (-1.2e3, 0.9e2) is_young_pelican = .false. ! pelican_tag = 'J' @@ -446,8 +446,8 @@ program variables !! Pelican pod tracking tag number_of_pelicans = 5 - pelican_weight = 2.5 ! kg - pelican_population_dynamics = (-1.2, 0.9) ! births, deaths per year + pelican_weight = 2.5 ! kg + pelican_population_dynamics = (-1.2e3, 0.9e2) ! births, deaths per year is_young_pelican = .false. print *, number_of_pelicans diff --git a/episodes/04-logic.md b/episodes/04-logic.md index 128fbebf..308f2a1b 100644 --- a/episodes/04-logic.md +++ b/episodes/04-logic.md @@ -17,7 +17,82 @@ exercises: 15 :::::::::::::::::::::::::::::::::::::::::::::::: -Variables store information we can use in our +Fortran's `logical` type has two values: + +```fortran + logical :: is_below_20_metres = .false. + logical :: use_stochastic_physics = .true. +``` + +### Logical operators + +Values can be tested logical operators `.or.`, `.and.` and `.not.` are available, and +these can be used to set the value of logical variables. + +The precedence is illustrated by, e.g., +```fortran + q = i .or. j .and. .not. k ! evaluated as i .or. (j .and. (.not. k)) +``` +where q, i, j, and k are all logical variables. + +Use brackets to avoid confusion over operator precedence. + +### Relational operators + +To form logical expressions from numeric or other expressions, we require +relational operators. The are two forms in Fortran, illustrated in the table +below. It is recommended that you avoid the older form. + +| Relation | Operator | Older form | For | +|--------------------------|----------|------------|------------------| +| Less than | `< ` | `.lt.` | `integer` `real` | +| Less than or equal to | `<=` | `.le.` | `integer` `real` | +| Greater than | `> ` | `.gt.` | `integer` `real` | +| Greater than or equal to | `>=` | `.ge.` | `integer` `real` | +| Equal to | `==` | `.eq.` | `integer` `real` `complex`| +| Not equal to | `/=` | `.neq.` | `integer` `real` `complex`| + +### Logical equivalence + +Equivalence between two logical expressions or variables is established +via the logical operators `.eqv.` and `.neqv.`. + +While some some compilers may allow the use of `==`, this should be avoided. + +### Using logical operators + +These operators can be used to check and set the values of logical variables, dependent on other variables, e.g. +```fortran +program example4 + implicit none + + real, parameter :: pi = 3.14159265 + logical, parameter :: switch1 = .true. + + real :: a=3.0 + logical :: test1, test2, test3 + + test1 = a >= pi ! True if a is greater than or equal to pi + + test2 = (.not. test1) ! True if test1 is False, False if test1 is True + + test3 = (test2 .eqv. switch1) ! True if test2 is True, False if test2 is False + + print *, test1 + + print *, test2 + + print *, test3 + +end program example4 +``` +Compiling and running this code will give the following output +``` +$ ./a.out + F + T + T +``` :::::::::::::::::::::::::::::::::::::::: keypoints diff --git a/episodes/data/exercises.tar.gz b/episodes/data/exercises.tar.gz index 427295d22315ca2c2b9bd7fad9ddd4a6bf6dc183..d6ab22c27e1b2f618178d37c6f4308c914f463c5 100644 GIT binary patch literal 5817 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 D38=A9 literal 5764 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