{ "cells": [ { "cell_type": "markdown", "id": "21326c2d-1aeb-45f5-a4f8-ce49901e15ba", "metadata": {}, "source": [ "\n", "" ] }, { "cell_type": "markdown", "id": "210449e5-31bb-4323-b21b-0be4a1b02d7a", "metadata": {}, "source": [ "# Types and Kinds\n", "\n", "\n", "```{contents}\n", ":local:\n", "```\n", "\n", "INTRO HERE?\n", "\n", "## Types\n", "\n", "There are five types in Fortran. They are:\n", "\n", "| Type | example |\n", "| ------------ | -------------------- |\n", "| integer | `1` |\n", "| real (float) | `1.0` or `1.2d-11` |\n", "| character | `'text'` |\n", "| complex | `( 1.0, 1.0 )` |\n", "| logical | `.true.` or `.false` |\n", "\n", "## Declaring Variables\n", "\n", "Variables can be declared using the `::` syntax:\n", "\n", "```fortran\n", "integer :: i, j ! simple counters for looping\n", "real :: my_height\n", "```\n", "\n", "Here we declared two integer variables, then a real variable. Notice how the `::` are aligned for readability. Variables names can contain letters, numbers, and `_` only. They must start with a letter.\n", "\n", "### implicit none\n", "\n", "Fortran assumes that the single character variables i through n will be used for counting during loops and automatically declares them as integer types. It is good practice in modern Fortran to declare `implicit none` at the start of all programs, modules, functions, and subroutines to supress this behaviour.\n", "\n", "## Assigning Variables\n", "\n", "Variables can be assigned when they are declared however it is best practice to assign a value with a new statement for instance:\n", "\n", "```fortran\n", "integer :: i, j ! simple counters for looping\n", "real :: my_height\n", "\n", "i = 0\n", "my-height = 54.7\n", "```\n", "\n", ":::{note}\n", ":name: assigning-vars\n", "Always ensure variables are assinged a value. If a variable is delcared and referenced but not assigned unexpected behaviour can occur:\n", "\n", "```fortran\n", "real :: y\n", "print *, y\n", "! gfortran prints 4.56472975E-41\n", "! ifort prints 0\n", "```\n", "\n", "Here the gfortran compiler printed what was already in the memory it assigned for y, whereas the ifort compiler set the value to zero.\n", ":::\n", "\n", "### Parameters\n", "\n", "Parameters are variables with a fixed value that cannot be changed. Parameters must be declared with their value:\n", "\n", "```fortran\n", "real :: earth_radius = 6371000\n", "character(*), parameter :: earth_radius_unit = 'm'\n", "```\n", "\n", "They are often used, as in the example above, for physical constants.\n", "\n", ":::{tip}\n", ":name: constants-pi\n", "Fortran doesn't have any built in constants such as $e$ or $\\pi$. To calculate these constants use:\n", "\n", "```fortran\n", "e = exp(1.0)\n", "pi = 4.0*atan(1.0)\n", "```\n", "\n", "From [Rosetta Code](https://rosettacode.org/wiki/Real_constants_and_functions#Fortran).\n", ":::\n", "\n", "### Character Type\n", "\n", "Characters are declared with a length variable. This allocates the correct amount of memory for the string length. If the string is shorter than the declared length it will be padded with spaces. If the string is longer than the declared length it will be cut to the declared length.\n", "\n", "::::{margin} \n", ":::{admonition} trim()\n", ":class: note\n", "When printing a character type the `trim()` command will remove any trailing whitespaces.\n", ":::\n", "::::\n", "\n", "
\n", "

Interactive

\n", "
\n", "

\n", " Make sure you have [activated interactivity]() on this page.\n", "

\n", " \n", "
\n", "
" ] }, { "cell_type": "code", "execution_count": null, "id": "f56313f6-006f-467e-b428-64ccebf05518", "metadata": {}, "outputs": [], "source": [ "character(len=7) :: space_ship = 'Voyager'\n", "print *, space_ship" ] }, { "cell_type": "markdown", "id": "5729af1b-eb5e-4ef4-bf54-16f9d85139d9", "metadata": {}, "source": [ ":::{note}\n", ":class: dropdown\n", "\n", "The `*` length declaration tells Fortran to automatically calculate the required length on assignment. [^1] This can only be used for dummy function arguments or parameters.\n", "\n", "[^1]: The length can also be automatically calculated on allocation `character(len=:), allocatable :: c`, see [memory management]() for information on allocatables.\n", "\n", ":::\n", "\n", "### data Statements\n", "\n", "In some older code you may come across the `data` statement. This statement causes the compiler to load some\n", "initial values into variables at compile time; it is a nonexecutable statement. It takes the general form:\n", "\n", "```fortran\n", "data varlist /vallist/ [, varlist /vallist/]\n", "```\n", "\n", "For example:\n", "\n", "```fortran\n", "data a,b,c / 1.,2.,3./\n", "data a,b,c / 1.,2.,3. /, X,Y,Z / 4.,5.,6. /\n", "data ra / 100*1.0 /\n", "```\n", "\n", ":::{note}\n", "Objects initialised by the `data` statement have the `save` attribute. [^2] \n", "It is recommended NOT to use the `data` statement in most cases in favour of the type delcaration statement !!!! ref to sec. [^3]\n", ":::\n", "\n", "[^2]: Data save attribute reference, [Adam Marshall](https://www.mrao.cam.ac.uk/~pa/f90Notes/HTMLNotesnode259.html).\n", "[^3]: page 140 of Modern Fortran Explained (the green edition from 2011 !!!!! ADD PROPER REF\n", "\n", "## Kinds\n", "\n", "The default precision for real numbers in Fortran is ***single*** precision (sp). You should take care when declaring any number to specify a kind (precision).\n", "\n", "To declare the precision of a number using `kind`:\n", "\n", "```fortran\n", " ! hardware specific 32 bit real (sp)\n", "integer, parameter :: r_32 = selected_real_kind(6, 37)\n", "! hardware specific 64 bit real\n", "integer, parameter :: r_64 = selected_real_kind(15, 307)\n", "\n", "! kind can be ommitted but this is not recommended\n", "real(r_32) :: height\n", "real(kind=r_64) :: earth_radius = 6371229.0_r_64\n", "```\n", "\n", "Here we defined our precisions as integer parameters to initialise our real numbers with. The real variable `height` is single precision whereas the real variable `earth_radius` is double precision (dp). Notice that we also assigned a number to `eath_radius` when we delcared it. This number ends in `_r_64` denoting it is a double precision real number. Without this Fortran will assign the number as single **not** double precision.\n", "\n", "There are other ways to declare the precision of a variable, you may see: `3.14d0` instead of `3.14_r_64` to denote double precision. You may also see kinds declared in byte units: `REAL*8`, this does not conform to the Fortran standard and should not be used.\n", "\n", "Where possible avoid using `d` and follow the example above to declare a variables kind.You may also see use of the intrinsic Fortran module **iso_fortran_env**. Kinds are declared with this module as below:\n", "\n", "```fortran\n", "! module use statement\n", "use, intrinsic :: iso_fortran_env, only: int64, real64, real128\n", "\n", "integer, parameter :: i_64 = int64\n", "integer, parameter :: r_64 = real64\n", "integer, parameter :: r_128 = real128\n", "\n", "real(kind=r_64) :: x = 1.0_r_64\n", "real(kind=r_64) :: y\n", "y = 1.0 ! single precision\n", "y = 1.0_r_64 ! double precision\n", "```\n", "\n", "In this snippet three kinds: a 64 bit integer, a 64 bit real, and a 128 bit real, were loaded. Again note the warning about assingning a double precision value to a declared variable. The use of intrinisc modules and how to create your own is dealt with in the [!!!!!!!!!!]() section.\n", "\n", ":::{note}\n", ":name: char-kind\n", "For character types the kind must precede the string and the kind relates to the character set used. This will not be explored further in this course.\n", "\n", "```fortran\n", "integer, parameter :: ck = kind('A')\n", "character(kind=ck, len=12) :: title\n", "\n", "title = ck_'The Good Place'\n", "```\n", "\n", ":::\n", "\n", "### Precision Tips\n", "\n", "This list may seem long but as you write more Fortran code they will become part of your standard practice.\n", "\n", "- DO ensure all real values have the correct kind declared\n", "- DO assign high precision constants \n", "- DO assign dp variables with a kind such as `r_64`, eg. `3.14_r_64`\n", "- DO use a module to store your kind declarations, see good practice in [constants_mod.f90]()\n", "\n", "- Avoid using compiler flags to set default precision, set explicitly using kinds\n", "- Avoid mixing types in arithmetic\n", "- Avoid converting from high to lower precision types\n", "\n", "### Converting Types & Kinds\n", "\n", "Converting between kinds is possible:\n", "\n", "```fortran\n", "real(x, sp) ! converts to the kind sp (single precision)\n", "real(x, dp) ! converts to the kind dp (double precision)\n", "```\n", "\n", ":::{tip}\n", ":name: real-to-int\n", "Use the intrinsic function `nint(x)` to convert a real number to the nearest integer.\n", ":::\n", "\n", "Conversion between other types and kinds is dealt with in the [Internal IO]() section.\n", "\n", "### Overflow and Underflow\n", "\n", "If a number becomes so large it cannot be represented anymore by your chosen kind then you have overflow and vice versa for underflow. To see the largest and smallest numbers a kind can represent use the functions:\n", "\n", "```fortran\n", "epsilon(x) ! 1.19209290E-07, effectively negligible number for x's kind\n", "huge(x) ! 3.40282347E+38, largest number\n", "tiny(x) ! 1.17549435E-38, smallest number\n", "```\n", "\n", "Here `x` is a double precision real and the example output, printed using `print *, tiny(x)` for instance, is for a typical 64 bit linux machine. To test for overflow `huge(x)` can be used:\n", "\n", "```fortran\n", "if ( real1 <= huge(real1) ) then\n", " ...\n", "else\n", " error stop 'Overflow'\n", "end if\n", "```\n", "\n", "To deal with underflow use `tiny(x)`:\n", "\n", "```fortran\n", "if ( abs(real1 - real2) < tiny(real1) ) then\n", " ...\n", "end if\n", "```\n", "\n", "## Summary\n", "\n", "## Exercises\n", "\n", ":::{exercise}\n", ":label: ex2\n", "\n", "Write a program that declares and initialises these variables:\n", "\n", "- Two integers\n", "- A single precision real\n", "- A double precision real\n", "- A parameter containing the value of $\\pi$\n", "- A complex number\n", "- Two character strings\n", "\n", "Be sure to use correct style (program name, no implicit typing, correct declarations).\n", "You will be able to use snippets from this program to help you with the next set of exercises.\n", ":::\n", "\n", ":::{solution} ex2\n", ":label: ex2-sol\n", ":class: dropdown\n", "\n", "``` fortran\n", "program ex2\n", "\n", " implicit none\n", " \n", " ! hardware specific 64 bit real\n", " integer, parameter :: r_64 = selected_real_kind(15, 307)\n", " \n", " integer :: apples, oranges ! integers\n", " real :: height ! sp then dp reals\n", " real(kind=r_64) :: age_universe\n", " real(kind=r_64), parameter :: pi = 3.14_r_64 ! parameter constant\n", " complex :: complex_num ! complex\n", " character(len=5) :: a ! characters\n", " ! length determined on assignment\n", " character(*), parameter :: b = 'the final frontier!'\n", " \n", " apples = 5\n", " oranges = 6\n", " \n", " height = 172.5 ! m\n", " age_universe = 13.772e9_r_64 ! years\n", " \n", " complex_num = ( 5.0, 3.5 )\n", " \n", " a = 'Space' \n", "\n", "end program ex2\n", "```\n", ":::" ] } ], "metadata": { "kernelspec": { "display_name": "Fortran", "language": "fortran", "name": "fortran" }, "language_info": { "file_extension": ".f90", "mimetype": "text/x-fortran", "name": "fortran", "version": "2018" } }, "nbformat": 4, "nbformat_minor": 5 }