Category: Fortran

  • Debugging Program

    A debugger tool is used to search for errors in the programs.

    A debugger program steps through the code and allows you to examine the values in the variables and other data objects during execution of the program.

    It loads the source code and you are supposed to run the program within the debugger. Debuggers debug a program by −

    • Setting breakpoints,
    • Stepping through the source code,
    • Setting watch points.

    Breakpoints specify where the program should stop, specifically after a critical line of code. Program executions after the variables are checked at a breakpoint.

    Debugger programs also check the source code line by line.

    Watch points are the points where the values of some variables are needed to be checked, particularly after a read or write operation.

    The gdb Debugger

    The gdb debugger, the GNU debugger comes with Linux operating system. For X windows system, gdb comes with a graphical interface and the program is named xxgdb.

    Following table provides some commands in gdb −

    CommandPurpose
    breakSetting a breakpoint
    runStarts execution
    contContinues execution
    nextExecutes only the next line of source code, without stepping into any function call
    stepExecute the next line of source code by stepping into a function in case of a function call.

    The dbx Debugger

    There is another debugger, the dbx debugger, for Linux.

    The following table provides some commands in dbx −

    CommandPurpose
    stop[var]Sets a breakpoint when the value of variable var changes.
    stop in [proc]It stops execution when a procedure proc is entered
    stop at [line]It sets a breakpoint at a specified line.
    runStarts execution.
    contContinues execution.
    nextExecutes only the next line of source code, without stepping into any function call.
    stepExecute the next line of source code by stepping into a function in case of a function call.
  • Programming Style

    Programming style is all about following some rules while developing programs. These good practices impart values like readability, and unambiguity into your program.

    A good program should have the following characteristics −

    • Readability
    • Proper logical structure
    • Self-explanatory notes and comments

    For example, if you make a comment like the following, it will not be of much help −

    ! loop from 1 to 10 
    do i = 1,10  

    However, if you are calculating binomial coefficient, and need this loop for nCr then a comment like this will be helpful −

    ! loop to calculate nCr 
    do i = 1,10
    • Indented code blocks to make various levels of code clear.
    • Self-checking codes to ensure there will be no numerical errors like division by zero, square root of a negative real number or logarithm of a negative real number.
    • Including codes that ensure variables do not take illegal or out of range values, i.e., input validation.
    • Not putting checks where it would be unnecessary and slows down the execution. For example −
    real :: x 
    x = sin(y) + 1.0
    
    if (x >= 0.0) then
       z = sqrt(x)
    end if
    • Clearly written code using appropriate algorithms.
    • Splitting the long expressions using the continuation marker ‘&’.
    • Making meaningful variable names.
  • Program Libraries

    There are various Fortran tools and libraries. Some are free and some are paid services.

    Following are some free libraries −

    • RANDLIB, random number and statistical distribution generators
    • BLAS
    • EISPACK
    • GAMS–NIST Guide to Available Math Software
    • Some statistical and other routines from NIST
    • LAPACK
    • LINPACK
    • MINPACK
    • MUDPACK
    • NCAR Mathematical Library
    • The Netlib collection of mathematical software, papers, and databases.
    • ODEPACK
    • ODERPACK, a set of routines for ranking and ordering.
    • Expokit for computing matrix exponentials
    • SLATEC
    • SPECFUN
    • STARPAC
    • StatLib statistical library
    • TOMS
    • Sorting and merging strings

    The following libraries are not free −

    • The NAG Fortran numerical library
    • The Visual Numerics IMSL library
    • Numerical Recipes
  • Numeric Precision

    We have already discussed that, in older versions of Fortran, there were two real types: the default real type and double precision type.

    However, Fortran 90/95 provides more control over the precision of real and integer data types through the kind specifie.

    The Kind Attribute

    Different kind of numbers are stored differently inside the computer. The kind attribute allows you to specify how a number is stored internally. For example,

    real, kind = 2 :: a, b, c
    real, kind = 4 :: e, f, g
    integer, kind = 2 :: i, j, k
    integer, kind = 3 :: l, m, n
    

    In the above declaration, the real variables e, f and g have more precision than the real variables a, b and c. The integer variables l, m and n, can store larger values and have more digits for storage than the integer variables i, j and k. Although this is machine dependent.

    Example

    Live Demo

    program kindSpecifier
    implicit none
    
       real(kind = 4) :: a, b, c
       real(kind = 8) :: e, f, g
       integer(kind = 2) :: i, j, k
       integer(kind = 4) :: l, m, n
       integer :: kind_a, kind_i, kind_e, kind_l
       
       kind_a = kind(a)
       kind_i = kind(i)
       kind_e = kind(e)
       kind_l = kind(l)
       
       print *,'default kind for real is', kind_a
       print *,'default kind for int is', kind_i
       print *,'extended kind for real is', kind_e
       print *,'default kind for int is', kind_l
       
    end program kindSpecifier

    When you compile and execute the above program it produces the following result −

    default kind for real is 4
    default kind for int is 2
    extended kind for real is 8
    default kind for int is 4
    

    Inquiring the Size of Variables

    There are a number of intrinsic functions that allows you to interrogate the size of numbers.

    For example, the bit_size(i) intrinsic function specifies the number of bits used for storage. For real numbers, the precision(x) intrinsic function, returns the number of decimal digits of precision, while the range(x) intrinsic function returns the decimal range of the exponent.

    Example

    Live Demo

    program getSize
    implicit none
    
       real (kind = 4) :: a
       real (kind = 8) :: b
       integer (kind = 2) :: i
       integer (kind = 4) :: j
    
       print *,'precision of real(4) =', precision(a)
       print *,'precision of real(8) =', precision(b)
       
       print *,'range of real(4) =', range(a)
       print *,'range of real(8) =', range(b)
       
    
       print *,'maximum exponent of real(4) =' , maxexponent(a)
       print *,'maximum exponent of real(8) =' , maxexponent(b)
      
       print *,'minimum exponent of real(4) =' , minexponent(a)
       print *,'minimum exponent of real(8) =' , minexponent(b)
       
       print *,'bits in integer(2) =' , bit_size(i)
       print *,'bits in integer(4) =' , bit_size(j)
       
    end program getSize

    When you compile and execute the above program it produces the following result −

    precision of real(4) = 6
    precision of real(8) = 15
    range of real(4) = 37
    range of real(8) = 307
    maximum exponent of real(4) = 128
    maximum exponent of real(8) = 1024
    minimum exponent of real(4) = -125
    minimum exponent of real(8) = -1021
    bits in integer(2) = 16
    bits in integer(4) = 32
    

    Obtaining the Kind Value

    Fortran provides two more intrinsic functions to obtain the kind value for the required precision of integers and reals −

    • selected_int_kind (r)
    • selected_real_kind ([p, r])

    The selected_real_kind function returns an integer that is the kind type parameter value necessary for a given decimal precision p and decimal exponent range r. The decimal precision is the number of significant digits, and the decimal exponent range specifies the smallest and largest representable number. The range is thus from 10-r to 10+r.

    For example, selected_real_kind (p = 10, r = 99) returns the kind value needed for a precision of 10 decimal places, and a range of at least 10-99 to 10+99.

    Example

    Live Demo

    program getKind
    implicit none
    
       integer:: i
       i = selected_real_kind (p = 10, r = 99) 
       print *,'selected_real_kind (p = 10, r = 99)', i
       
    end program getKind

    When you compile and execute the above program it produces the following result −

    selected_real_kind (p = 10, r = 99) 8
    
  • Intrinsic Functions

    Intrinsic functions are some common and important functions that are provided as a part of the Fortran language. We have already discussed some of these functions in the Arrays, Characters and String chapters.

    Intrinsic functions can be categorised as −

    • Numeric Functions
    • Mathematical Functions
    • Numeric Inquiry Functions
    • Floating-Point Manipulation Functions
    • Bit Manipulation Functions
    • Character Functions
    • Kind Functions
    • Logical Functions
    • Array Functions.

    We have discussed the array functions in the Arrays chapter. In the following section we provide brief descriptions of all these functions from other categories.

    In the function name column,

    • A represents any type of numeric variable
    • R represents a real or integer variable
    • X and Y represent real variables
    • Z represents complex variable
    • W represents real or complex variable

    Numeric Functions

    Sr.NoFunction & Description
    1ABS (A)It returns the absolute value of A
    2AIMAG (Z)It returns the imaginary part of a complex number Z
    3AINT (A [, KIND])It truncates fractional part of A towards zero, returning a real, whole number.
    4ANINT (A [, KIND])It returns a real value, the nearest integer or whole number.
    5CEILING (A [, KIND])It returns the least integer greater than or equal to number A.
    6CMPLX (X [, Y, KIND])It converts the real variables X and Y to a complex number X+iY; if Y is absent, 0 is used.
    7CONJG (Z)It returns the complex conjugate of any complex number Z.
    8DBLE (A)It converts A to a double precision real number.
    9DIM (X, Y)It returns the positive difference of X and Y.
    10DPROD (X, Y)It returns the double precision real product of X and Y.
    11FLOOR (A [, KIND])It provides the greatest integer less than or equal to number A.
    12INT (A [, KIND])It converts a number (real or integer) to integer, truncating the real part towards zero.
    13MAX (A1, A2 [, A3,…])It returns the maximum value from the arguments, all being of same type.
    14MIN (A1, A2 [, A3,…])It returns the minimum value from the arguments, all being of same type.
    15MOD (A, P)It returns the remainder of A on division by P, both arguments being of the same type (A-INT(A/P)*P)
    16MODULO (A, P)It returns A modulo P: (A-FLOOR(A/P)*P)
    17NINT (A [, KIND])It returns the nearest integer of number A
    18REAL (A [, KIND])It Converts to real type
    19SIGN (A, B)It returns the absolute value of A multiplied by the sign of P. Basically it transfers the of sign of B to A.

    Example

    program numericFunctions
    implicit none  
    
       ! define constants  
       ! define variables
       real :: a, b 
       complex :: z
       
       ! values for a, b 
       a = 15.2345
       b = -20.7689
    
    write(*,*) 'abs(a): ',abs(a),' abs(b): ',abs(b) write(*,*) 'aint(a): ',aint(a),' aint(b): ',aint(b) write(*,*) 'ceiling(a): ',ceiling(a),' ceiling(b): ',ceiling(b) write(*,*) 'floor(a): ',floor(a),' floor(b): ',floor(b)
    z = cmplx(a, b) write(*,*) 'z: ',z end program numericFunctions

    When you compile and execute the above program, it produces the following result −

    abs(a): 15.2344999   abs(b): 20.7688999    
    aint(a): 15.0000000  aint(b): -20.0000000    
    ceiling(a): 16  ceiling(b): -20
    floor(a): 15  floor(b): -21
    z: (15.2344999, -20.7688999)
    

    Mathematical Functions

    Sr.NoFunction & Description
    1ACOS (X)It returns the inverse cosine in the range (0, π), in radians.
    2ASIN (X)It returns the inverse sine in the range (-π/2, π/2), in radians.
    3ATAN (X)It returns the inverse tangent in the range (-π/2, π/2), in radians.
    4ATAN2 (Y, X)It returns the inverse tangent in the range (-π, π), in radians.
    5COS (X)It returns the cosine of argument in radians.
    6COSH (X)It returns the hyperbolic cosine of argument in radians.
    7EXP (X)It returns the exponential value of X.
    8LOG (X)It returns the natural logarithmic value of X.
    9LOG10 (X)It returns the common logarithmic (base 10) value of X.
    10SIN (X)It returns the sine of argument in radians.
    11SINH (X)It returns the hyperbolic sine of argument in radians.
    12SQRT (X)It returns square root of X.
    13TAN (X)It returns the tangent of argument in radians.
    14TANH (X)It returns the hyperbolic tangent of argument in radians.

    Example

    The following program computes the horizontal and vertical position x and y respectively of a projectile after a time, t −

    Where, x = u t cos a and y = u t sin a - g t2 / 2
    program projectileMotion  
    implicit none  
    
       ! define constants  
       real, parameter :: g = 9.8  
       real, parameter :: pi = 3.1415927  
       
       !define variables
       real :: a, t, u, x, y   
       
       !values for a, t, and u 
       a = 45.0
       t = 20.0
       u = 10.0
       
       ! convert angle to radians  
       a = a * pi / 180.0  
       x = u * cos(a) * t   
       y = u * sin(a) * t - 0.5 * g * t * t  
       
       write(*,*) 'x: ',x,'  y: ',y   
       
    end program projectileMotion

    When you compile and execute the above program, it produces the following result −

    x: 141.421356  y: -1818.57861  
    

    Numeric Inquiry Functions

    These functions work with a certain model of integer and floating-point arithmetic. The functions return properties of numbers of the same kind as the variable X, which can be real and in some cases integer.

    Sr.NoFunction & Description
    1DIGITS (X)It returns the number of significant digits of the model.
    2EPSILON (X)It returns the number that is almost negligible compared to one. In other words, it returns the smallest value such that REAL( 1.0, KIND(X)) + EPSILON(X) is not equal to REAL( 1.0, KIND(X)).
    3HUGE (X)It returns the largest number of the model
    4MAXEXPONENT (X)It returns the maximum exponent of the model
    5MINEXPONENT (X)It returns the minimum exponent of the model
    6PRECISION (X)It returns the decimal precision
    7RADIX (X)It returns the base of the model
    8RANGE (X)It returns the decimal exponent range
    9TINY (X)It returns the smallest positive number of the model

    Floating-Point Manipulation Functions

    Sr.NoFunction & Description
    1EXPONENT (X)It returns the exponent part of a model number
    2FRACTION (X)It returns the fractional part of a number
    3NEAREST (X, S)It returns the nearest different processor number in given direction
    4RRSPACING (X)It returns the reciprocal of the relative spacing of model numbers near given number
    5SCALE (X, I)It multiplies a real by its base to an integer power
    6SET_EXPONENT (X, I)it returns the exponent part of a number
    7SPACING (X)It returns the absolute spacing of model numbers near given number

    Bit Manipulation Functions

    Sr.NoFunction & Description
    1BIT_SIZE (I)It returns the number of bits of the model
    2BTEST (I, POS)Bit testing
    3IAND (I, J)Logical AND
    4IBCLR (I, POS)Clear bit
    5IBITS (I, POS, LEN)Bit extraction
    6IBSET (I, POS)Set bit
    7IEOR (I, J)Exclusive OR
    8IOR (I, J)Inclusive OR
    9ISHFT (I, SHIFT)Logical shift
    10ISHFTC (I, SHIFT [, SIZE])Circular shift
    11NOT (I)Logical complement

    Character Functions

    Sr.NoFunction & Description
    1ACHAR (I)It returns the Ith character in the ASCII collating sequence.
    2ADJUSTL (STRING)It adjusts string left by removing any leading blanks and inserting trailing blanks
    3ADJUSTR (STRING)It adjusts string right by removing trailing blanks and inserting leading blanks.
    4CHAR (I [, KIND])It returns the Ith character in the machine specific collating sequence
    5IACHAR (C)It returns the position of the character in the ASCII collating sequence.
    6ICHAR (C)It returns the position of the character in the machine (processor) specific collating sequence.
    7INDEX (STRING, SUBSTRING [, BACK])It returns the leftmost (rightmost if BACK is .TRUE.) starting position of SUBSTRING within STRING.
    8LEN (STRING)It returns the length of a string.
    9LEN_TRIM (STRING)It returns the length of a string without trailing blank characters.
    10LGE (STRING_A, STRING_B)Lexically greater than or equal
    11LGT (STRING_A, STRING_B)Lexically greater than
    12LLE (STRING_A, STRING_B)Lexically less than or equal
    13LLT (STRING_A, STRING_B)Lexically less than
    14REPEAT (STRING, NCOPIES)Repeated concatenation
    15SCAN (STRING, SET [, BACK])It returns the index of the leftmost (rightmost if BACK is .TRUE.) character of STRING that belong to SET, or 0 if none belong.
    16TRIM (STRING)Removes trailing blank characters
    17VERIFY (STRING, SET [, BACK])Verifies the set of characters in a string

    AD

    https://delivery.adrecover.com/recover.html?siteId=18107&dataDogLoggingEnabled=false&dataDogLoggingVersion=1

    Kind Functions

    Sr.NoFunction & Description
    1KIND (X)It returns the kind type parameter value.
    2SELECTED_INT_KIND (R)It returns kind of type parameter for specified exponent range.
    3SELECTED_REAL_KIND ([P, R])Real kind type parameter value, given precision and range

    Logical Function

    Sr.NoFunction & Description
    1LOGICAL (L [, KIND])Convert between objects of type logical with different kind type parameters
  • Modules

    A module is like a package where you can keep your functions and subroutines, in case you are writing a very big program, or your functions or subroutines can be used in more than one program.

    Modules provide you a way of splitting your programs between multiple files.

    Modules are used for −

    • Packaging subprograms, data and interface blocks.
    • Defining global data that can be used by more than one routine.
    • Declaring variables that can be made available within any routines you choose.
    • Importing a module entirely, for use, into another program or subroutine.

    Syntax of a Module

    A module consists of two parts −

    • a specification part for statements declaration
    • a contains part for subroutine and function definitions

    The general form of a module is −

    module name     
       

    [contains [subroutine and function definitions] ] end module [name]

    Using a Module into your Program

    You can incorporate a module in a program or subroutine by the use statement −

    use name  
    

    Please note that

    • You can add as many modules as needed, each will be in separate files and compiled separately.
    • A module can be used in various different programs.
    • A module can be used many times in the same program.
    • The variables declared in a module specification part, are global to the module.
    • The variables declared in a module become global variables in any program or routine where the module is used.
    • The use statement can appear in the main program, or any other subroutine or module which uses the routines or variables declared in a particular module.

    Example

    The following example demonstrates the concept −

    Live Demo

    module constants  
    implicit none 
    
       real, parameter :: pi = 3.1415926536  
       real, parameter :: e = 2.7182818285 
       
    contains      
       subroutine show_consts()          
    
      print*, "Pi = ", pi          
      print*,  "e = ", e     
    end subroutine show_consts end module constants program module_example use constants implicit none real :: x, ePowerx, area, radius x = 2.0 radius = 7.0 ePowerx = e ** x area = pi * radius**2 call show_consts() print*, "e raised to the power of 2.0 = ", ePowerx print*, "Area of a circle with radius 7.0 = ", area end program module_example

    When you compile and execute the above program, it produces the following result −

    Pi = 3.14159274    
    e =  2.71828175    
    e raised to the power of 2.0 = 7.38905573    
    Area of a circle with radius 7.0 = 153.938049   
    

    Accessibility of Variables and Subroutines in a Module

    By default, all the variables and subroutines in a module is made available to the program that is using the module code, by the use statement.

    However, you can control the accessibility of module code using the private and public attributes. When you declare some variable or subroutine as private, it is not available outside the module.

    Example

    The following example illustrates the concept −

    In the previous example, we had two module variables, e and pi. Let us make them private and observe the output −

    Live Demo

    module constants  
    implicit none 
    
       real, parameter,private :: pi = 3.1415926536  
       real, parameter, private :: e = 2.7182818285 
       
    contains      
       subroutine show_consts()          
    
      print*, "Pi = ", pi          
      print*, "e = ", e     
    end subroutine show_consts end module constants program module_example use constants implicit none real :: x, ePowerx, area, radius x = 2.0 radius = 7.0 ePowerx = e ** x area = pi * radius**2 call show_consts() print*, "e raised to the power of 2.0 = ", ePowerx print*, "Area of a circle with radius 7.0 = ", area end program module_example

    When you compile and execute the above program, it gives the following error message −

       ePowerx = e ** x
       1
    Error: Symbol 'e' at (1) has no IMPLICIT type
    main.f95:19.13:
    
       area = pi * radius**2     
       1
    Error: Symbol 'pi' at (1) has no IMPLICIT type
    

    Since e and pi, both are declared private, the program module_example cannot access these variables anymore.

    However, other module subroutines can access them −

    Live Demo

    module constants  
    implicit none 
    
       real, parameter,private :: pi = 3.1415926536  
       real, parameter, private :: e = 2.7182818285 
       
    contains      
       subroutine show_consts()          
    
      print*, "Pi = ", pi          
      print*, "e = ", e     
    end subroutine show_consts function ePowerx(x)result(ePx) implicit none
      real::x
      real::ePx
      ePx = e ** x
    end function ePowerx
    function areaCircle(r)result(a) implicit none
      real::r
      real::a
      a = pi * r**2  
    end function areaCircle
    end module constants program module_example use constants implicit none call show_consts() Print*, "e raised to the power of 2.0 = ", ePowerx(2.0) print*, "Area of a circle with radius 7.0 = ", areaCircle(7.0) end program module_example

    When you compile and execute the above program, it produces the following result −

    Pi = 3.14159274    
    e = 2.71828175    
    e raised to the power of 2.0 = 7.38905573    
    Area of a circle with radius 7.0 = 153.938049   
    
  • Procedures

    procedure is a group of statements that perform a well-defined task and can be invoked from your program. Information (or data) is passed to the calling program, to the procedure as arguments.

    There are two types of procedures −

    • Functions
    • Subroutines

    Function

    A function is a procedure that returns a single quantity. A function should not modify its arguments.

    The returned quantity is known as function value, and it is denoted by the function name.

    Syntax

    Syntax for a function is as follows −

    function name(arg1, arg2, ....)  
       

    end function [name]

    The following example demonstrates a function named area_of_circle. It calculates the area of a circle with radius r.

    program calling_func
    
       real :: a
       a = area_of_circle(2.0) 
       
       Print *, "The area of a circle with radius 2.0 is"
       Print *, a
       
    end program calling_func
    
    
    ! this function computes the area of a circle with radius r  
    function area_of_circle (r)  
    
    ! function result     
    implicit none      
    
       ! dummy arguments        
       real :: area_of_circle   
       
       ! local variables 
       real :: r     
       real :: pi
       
       pi = 4 * atan (1.0)     
       area_of_circle = pi * r**2  
       
    end function area_of_circle

    When you compile and execute the above program, it produces the following result −

    The area of a circle with radius 2.0 is
       12.5663710   
    

    Please note that −

    • You must specify implicit none in both the main program as well as the procedure.
    • The argument r in the called function is called dummy argument.

    The result Option

    If you want the returned value to be stored in some other name than the function name, you can use the result option.

    You can specify the return variable name as −

    function name(arg1, arg2, ....) result (return_var_name)  
       

    Subroutine

    A subroutine does not return a value, however it can modify its arguments.

    Syntax

    subroutine name(arg1, arg2, ....)    
       

    end subroutine [name]

    Calling a Subroutine

    You need to invoke a subroutine using the call statement.

    The following example demonstrates the definition and use of a subroutine swap, that changes the values of its arguments.

    Live Demo

    program calling_func
    implicit none
    
       real :: a, b
       a = 2.0
       b = 3.0
       
       Print *, "Before calling swap"
       Print *, "a = ", a
       Print *, "b = ", b
       
       call swap(a, b)
       
       Print *, "After calling swap"
       Print *, "a = ", a
       Print *, "b = ", b
       
    end program calling_func
    
    
    subroutine swap(x, y) 
    implicit none
    
       real :: x, y, temp   
       
       temp = x  
       x = y 
       y = temp  
       
    end subroutine swap

    When you compile and execute the above program, it produces the following result −

    Before calling swap
    a = 2.00000000    
    b = 3.00000000    
    After calling swap
    a = 3.00000000    
    b = 2.00000000   
    

    Specifying the Intent of the Arguments

    The intent attribute allows you to specify the intention with which arguments are used in the procedure. The following table provides the values of the intent attribute −

    ValueUsed asExplanation
    inintent(in)Used as input values, not changed in the function
    outintent(out)Used as output value, they are overwritten
    inoutintent(inout)Arguments are both used and overwritten

    The following example demonstrates the concept −

    Live Demo

    program calling_func
    implicit none
    
       real :: x, y, z, disc
       
       x = 1.0
       y = 5.0
       z = 2.0
       
       call intent_example(x, y, z, disc)
       
       Print *, "The value of the discriminant is"
       Print *, disc
       
    end program calling_func
    
    
    subroutine intent_example (a, b, c, d)     
    implicit none     
    
       ! dummy arguments      
       real, intent (in) :: a     
       real, intent (in) :: b      
       real, intent (in) :: c    
       real, intent (out) :: d   
       
       d = b * b - 4.0 * a * c 
       
    end subroutine intent_example

    When you compile and execute the above program, it produces the following result −

    The value of the discriminant is
       17.0000000    
    

    Recursive Procedures

    Recursion occurs when a programming languages allows you to call a function inside the same function. It is called recursive call of the function.

    When a procedure calls itself, directly or indirectly, is called a recursive procedure. You should declare this type of procedures by preceding the word recursive before its declaration.

    When a function is used recursively, the result option has to be used.

    Following is an example, which calculates factorial for a given number using a recursive procedure −

    program calling_func
    implicit none
    
       integer :: i, f
       i = 15
       
       Print *, "The value of factorial 15 is"
       f = myfactorial(15)
       Print *, f
       
    end program calling_func
    
    ! computes the factorial of n (n!)      
    recursive function myfactorial (n) result (fac)  
    ! function result     
    implicit none     
    
       ! dummy arguments     
       integer :: fac     
       integer, intent (in) :: n     
       
       select case (n)         
    
      case (0:1)         
         fac = 1         
      case default    
         fac = n * myfactorial (n-1)  
    end select end function myfactorial

    Internal Procedures

    When a procedure is contained within a program, it is called the internal procedure of the program. The syntax for containing an internal procedure is as follows −

    program program_name     
       implicit none         
       ! type declaration statements         
       ! executable statements    
       . . .     
       contains         
       ! internal procedures      
       . . .  
    end program program_name
    

    The following example demonstrates the concept −

    program mainprog  
    implicit none 
    
       real :: a, b 
       a = 2.0
       b = 3.0
       
       Print *, "Before calling swap"
       Print *, "a = ", a
       Print *, "b = ", b
       
       call swap(a, b)
       
       Print *, "After calling swap"
       Print *, "a = ", a
       Print *, "b = ", b
     
    contains   
       subroutine swap(x, y)     
    
      real :: x, y, temp      
      temp = x 
      x = y  
      y = temp   
    end subroutine swap end program mainprog

    When you compile and execute the above program, it produces the following result −

    Before calling swap
    a = 2.00000000    
    b = 3.00000000    
    After calling swap
    a = 3.00000000    
    b = 2.00000000   
    
  • File Input Output

    In the last chapter, you have seen how to read data from, and write data to the terminal. In this chapter you will study file input and output functionalities provided by Fortran.

    You can read and write to one or more files. The OPEN, WRITE, READ and CLOSE statements allow you to achieve this.

    Opening and Closing Files

    Before using a file you must open the file. The open command is used to open files for reading or writing. The simplest form of the command is −

    open (unit = number, file = "name").
    

    However, the open statement may have a general form −

    open (list-of-specifiers)
    

    The following table describes the most commonly used specifiers −

    Sr.NoSpecifier & Description
    1[UNIT=] uThe unit number u could be any number in the range 9-99 and it indicates the file, you may choose any number but every open file in the program must have a unique number
    2IOSTAT= iosIt is the I/O status identifier and should be an integer variable. If the open statement is successful then the ios value returned is zero else a non-zero value.
    3ERR = errIt is a label to which the control jumps in case of any error.
    4FILE = fnameFile name, a character string.
    5STATUS = staIt shows the prior status of the file. A character string and can have one of the three values NEW, OLD or SCRATCH. A scratch file is created and deleted when closed or the program ends.
    6ACCESS = accIt is the file access mode. Can have either of the two values, SEQUENTIAL or DIRECT. The default is SEQUENTIAL.
    7FORM = frmIt gives the formatting status of the file. Can have either of the two values FORMATTED or UNFORMATTED. The default is UNFORMATTED
    8RECL = rlIt specifies the length of each record in a direct access file.

    After the file has been opened, it is accessed by read and write statements. Once done, it should be closed using the close statement.

    The close statement has the following syntax −

    close ([UNIT = ]u[,IOSTAT = ios,ERR = err,STATUS = sta])
    

    Please note that the parameters in brackets are optional.

    Example

    This example demonstrates opening a new file for writing some data into the file.

    program outputdata   
    implicit none
    
       real, dimension(100) :: x, y  
       real, dimension(100) :: p, q
       integer :: i  
       
       ! data  
       do i=1,100  
    
      x(i) = i * 0.1 
      y(i) = sin(x(i)) * (1-cos(x(i)/3.0))  
    end do ! output data into a file open(1, file = 'data1.dat', status = 'new') do i=1,100
      write(1,*) x(i), y(i)   
    end do close(1) end program outputdata

    When the above code is compiled and executed, it creates the file data1.dat and writes the x and y array values into it. And then closes the file.

    Reading from and Writing into the File

    The read and write statements respectively are used for reading from and writing into a file respectively.

    They have the following syntax −

    read ([UNIT = ]u, [FMT = ]fmt, IOSTAT = ios, ERR = err, END = s)
    write([UNIT = ]u, [FMT = ]fmt, IOSTAT = ios, ERR = err, END = s)
    

    Most of the specifiers have already been discussed in the above table.

    The END = s specifier is a statement label where the program jumps, when it reaches end-of-file.

    Example

    This example demonstrates reading from and writing into a file.

    In this program we read from the file, we created in the last example, data1.dat, and display it on screen.

    Live Demo

    program outputdata   
    implicit none   
    
       real, dimension(100) :: x, y  
       real, dimension(100) :: p, q
       integer :: i  
       
       ! data  
       do i = 1,100  
    
      x(i) = i * 0.1 
      y(i) = sin(x(i)) * (1-cos(x(i)/3.0))  
    end do ! output data into a file open(1, file = 'data1.dat', status='new') do i = 1,100
      write(1,*) x(i), y(i)   
    end do close(1) ! opening the file for reading open (2, file = 'data1.dat', status = 'old') do i = 1,100
      read(2,*) p(i), q(i)
    end do close(2) do i = 1,100
      write(*,*) p(i), q(i)
    end do end program outputdata

    When the above code is compiled and executed, it produces the following result −

    0.100000001  5.54589933E-05
    0.200000003  4.41325130E-04
    0.300000012  1.47636665E-03
    0.400000006  3.45637114E-03
    0.500000000  6.64328877E-03
    0.600000024  1.12552457E-02
    0.699999988  1.74576249E-02
    0.800000012  2.53552198E-02
    0.900000036  3.49861123E-02
    1.00000000   4.63171229E-02
    1.10000002   5.92407547E-02
    1.20000005   7.35742599E-02
    1.30000007   8.90605897E-02
    1.39999998   0.105371222    
    1.50000000   0.122110792    
    1.60000002   0.138823599    
    1.70000005   0.155002072    
    1.80000007   0.170096487    
    1.89999998   0.183526158    
    2.00000000   0.194692180    
    2.10000014   0.202990443    
    2.20000005   0.207826138    
    2.29999995   0.208628103    
    2.40000010   0.204863414    
    2.50000000   0.196052119    
    2.60000014   0.181780845    
    2.70000005   0.161716297    
    2.79999995   0.135617107    
    2.90000010   0.103344671    
    3.00000000   6.48725405E-02
    3.10000014   2.02930309E-02
    3.20000005  -3.01767997E-02
    3.29999995  -8.61928314E-02
    3.40000010  -0.147283033    
    3.50000000  -0.212848678    
    3.60000014  -0.282169819    
    3.70000005  -0.354410470    
    3.79999995  -0.428629100    
    3.90000010  -0.503789663    
    4.00000000  -0.578774154    
    4.09999990  -0.652400017    
    4.20000029  -0.723436713    
    4.30000019  -0.790623367    
    4.40000010  -0.852691114    
    4.50000000  -0.908382416    
    4.59999990  -0.956472993    
    4.70000029  -0.995793998    
    4.80000019  -1.02525222    
    4.90000010  -1.04385209    
    5.00000000  -1.05071592    
    5.09999990  -1.04510069    
    5.20000029  -1.02641726    
    5.30000019  -0.994243503    
    5.40000010  -0.948338211    
    5.50000000  -0.888650239    
    5.59999990  -0.815326691    
    5.70000029  -0.728716135    
    5.80000019  -0.629372001    
    5.90000010  -0.518047631    
    6.00000000  -0.395693362    
    6.09999990  -0.263447165    
    6.20000029  -0.122622721    
    6.30000019   2.53026206E-02
    6.40000010   0.178709000    
    6.50000000   0.335851669    
    6.59999990   0.494883657    
    6.70000029   0.653881252    
    6.80000019   0.810866773    
    6.90000010   0.963840425    
    7.00000000   1.11080539    
    7.09999990   1.24979746    
    7.20000029   1.37891412    
    7.30000019   1.49633956    
    7.40000010   1.60037732    
    7.50000000   1.68947268    
    7.59999990   1.76223695    
    7.70000029   1.81747139    
    7.80000019   1.85418403    
    7.90000010   1.87160957    
    8.00000000   1.86922085    
    8.10000038   1.84674001    
    8.19999981   1.80414569    
    8.30000019   1.74167395    
    8.40000057   1.65982044    
    8.50000000   1.55933595    
    8.60000038   1.44121361    
    8.69999981   1.30668485    
    8.80000019   1.15719533    
    8.90000057   0.994394958    
    9.00000000   0.820112705    
    9.10000038   0.636327863    
    9.19999981   0.445154816    
    9.30000019   0.248800844    
    9.40000057   4.95488606E-02
    9.50000000  -0.150278628    
    9.60000038  -0.348357052    
    9.69999981  -0.542378068    
    9.80000019  -0.730095863    
    9.90000057  -0.909344316    
    10.0000000  -1.07807255    
    
  • Pointers

    In most programming languages, a pointer variable stores the memory address of an object. However, in Fortran, a pointer is a data object that has more functionalities than just storing the memory address. It contains more information about a particular object, like type, rank, extents, and memory address.

    A pointer is associated with a target by allocation or pointer assignment.

    Declaring a Pointer Variable

    A pointer variable is declared with the pointer attribute.

    The following examples shows declaration of pointer variables −

    integer, pointer :: p1 ! pointer to integer  
    real, pointer, dimension (:) :: pra ! pointer to 1-dim real array  
    real, pointer, dimension (:,:) :: pra2 ! pointer to 2-dim real array

    A pointer can point to −

    • An area of dynamically allocated memory.
    • A data object of the same type as the pointer, with the target attribute.

    Allocating Space for a Pointer

    The allocate statement allows you to allocate space for a pointer object. For example −

    program pointerExample
    implicit none
    
       integer, pointer :: p1
       allocate(p1)
       
       p1 = 1
       Print *, p1
       
       p1 = p1 + 4
       Print *, p1
       
    end program pointerExample

    When the above code is compiled and executed, it produces the following result −

    1
    5
    

    You should empty the allocated storage space by the deallocate statement when it is no longer required and avoid accumulation of unused and unusable memory space.

    Targets and Association

    A target is another normal variable, with space set aside for it. A target variable must be declared with the target attribute.

    You associate a pointer variable with a target variable using the association operator (=>).

    Let us rewrite the previous example, to demonstrate the concept −

    Live Demo

    program pointerExample
    implicit none
    
       integer, pointer :: p1
       integer, target :: t1 
       
       p1=>t1
       p1 = 1
       
       Print *, p1
       Print *, t1
       
       p1 = p1 + 4
       
       Print *, p1
       Print *, t1
       
       t1 = 8
       
       Print *, p1
       Print *, t1
       
    end program pointerExample

    When the above code is compiled and executed, it produces the following result −

    1
    1
    5
    5
    8
    8
    

    A pointer can be −

    • Undefined
    • Associated
    • Disassociated

    In the above program, we have associated the pointer p1, with the target t1, using the => operator. The function associated, tests a pointer’s association status.

    The nullify statement disassociates a pointer from a target.

    Nullify does not empty the targets as there could be more than one pointer pointing to the same target. However, emptying the pointer implies nullification also.

    Example 1

    The following example demonstrates the concepts −

    Live Demo

    program pointerExample
    implicit none
    
       integer, pointer :: p1
       integer, target :: t1 
       integer, target :: t2
       
       p1=>t1
       p1 = 1
       
       Print *, p1
       Print *, t1
       
       p1 = p1 + 4
       Print *, p1
       Print *, t1
       
       t1 = 8
       Print *, p1
       Print *, t1
       
       nullify(p1)
       Print *, t1
       
       p1=>t2
       Print *, associated(p1)
       Print*, associated(p1, t1)
       Print*, associated(p1, t2)
       
       !what is the value of p1 at present
       Print *, p1
       Print *, t2
       
       p1 = 10
       Print *, p1
       Print *, t2
       
    end program pointerExample

    When the above code is compiled and executed, it produces the following result −

    1
    1
    5
    5
    8
    8
    8
    T
    F
    T
    0
    0
    10
    10
    

    Please note that each time you run the code, the memory addresses will be different.

    Example 2

    Live Demo

    program pointerExample
    implicit none
    
       integer, pointer :: a, b
       integer, target :: t
       integer :: n
       
       t = 1
       a => t
       t = 2
       b => t
       n = a + b
       
       Print *, a, b, t, n 
       
    end program pointerExample

    When the above code is compiled and executed, it produces the following result −

    2  2  2  4
    
  • Derived Data Types

    Fortran allows you to define derived data types. A derived data type is also called a structure, and it can consist of data objects of different types.

    Derived data types are used to represent a record. E.g. you want to keep track of your books in a library, you might want to track the following attributes about each book −

    • Title
    • Author
    • Subject
    • Book ID

    Defining a Derived data type

    To define a derived data type, the type and end type statements are used. . The type statement defines a new data type, with more than one member for your program. The format of the type statement is this −

    type type_name      
       declarations
    end type 

    Here is the way you would declare the Book structure −

    type Books
       character(len = 50) :: title
       character(len = 50) :: author
       character(len = 150) :: subject
       integer :: book_id
    end type Books
    

    Accessing Structure Members

    An object of a derived data type is called a structure.

    A structure of type Books can be created in a type declaration statement like −

    type(Books) :: book1 
    

    The components of the structure can be accessed using the component selector character (%) −

    book1%title = "C Programming"
    book1%author = "Nuha Ali"
    book1%subject = "C Programming Tutorial"
    book1%book_id = 6495407
    

    Note that there are no spaces before and after the % symbol.

    Example

    The following program illustrates the above concepts −

    Live Demo

    program deriveDataType
    
       !type declaration
       type Books
    
      character(len = 50) :: title
      character(len = 50) :: author
      character(len = 150) :: subject
      integer :: book_id
    end type Books !declaring type variables type(Books) :: book1 type(Books) :: book2 !accessing the components of the structure book1%title = "C Programming" book1%author = "Nuha Ali" book1%subject = "C Programming Tutorial" book1%book_id = 6495407 book2%title = "Telecom Billing" book2%author = "Zara Ali" book2%subject = "Telecom Billing Tutorial" book2%book_id = 6495700 !display book info Print *, book1%title Print *, book1%author Print *, book1%subject Print *, book1%book_id Print *, book2%title Print *, book2%author Print *, book2%subject Print *, book2%book_id end program deriveDataType

    When the above code is compiled and executed, it produces the following result −

     C Programming                                     
     Nuha Ali                                          
     C Programming Tutorial            
       6495407
     Telecom Billing                                   
     Zara Ali                                          
     Telecom Billing Tutorial            
       6495700
    

    Array of Structures

    You can also create arrays of a derived type −

    type(Books), dimension(2) :: list
    

    Individual elements of the array could be accessed as −

    list(1)%title = "C Programming"
    list(1)%author = "Nuha Ali"
    list(1)%subject = "C Programming Tutorial"
    list(1)%book_id = 6495407
    

    The following program illustrates the concept −

    Live Demo

    program deriveDataType
    
       !type declaration
       type Books
    
      character(len = 50) :: title
      character(len = 50) :: author
      character(len = 150) :: subject
      integer :: book_id
    end type Books !declaring array of books type(Books), dimension(2) :: list
    !accessing the components of the structure list(1)%title = "C Programming" list(1)%author = "Nuha Ali" list(1)%subject = "C Programming Tutorial" list(1)%book_id = 6495407 list(2)%title = "Telecom Billing" list(2)%author = "Zara Ali" list(2)%subject = "Telecom Billing Tutorial" list(2)%book_id = 6495700 !display book info Print *, list(1)%title Print *, list(1)%author Print *, list(1)%subject Print *, list(1)%book_id Print *, list(1)%title Print *, list(2)%author Print *, list(2)%subject Print *, list(2)%book_id end program deriveDataType

    When the above code is compiled and executed, it produces the following result −

    C Programming                                     
    Nuha Ali                                          
    C Programming Tutorial               
       6495407
    C Programming                                     
    Zara Ali                                          
    Telecom Billing Tutorial                                      
       6495700