Documente Academic
Documente Profesional
Documente Cultură
Fortran 90
See next foil for copyright information
Nick Maclaren
Computing Service
November 2007
Introduction to Programming with Fortran 90 – p. 1/??
Acknowledgement
Derived from the course written and owned by
http://www-uxsup.csx.cam.ac.uk/courses/. . .
. . ./Fortran . . ./OldFortran
. . ./Arithmetic etc.
Fortran 90 Programming
by Miles Ellis, Ivor Phillips and
Thomas Lahey
http://www.fortran.com/fortran/
⇒ ‘Information’, ‘Standards Documents’
Liverpool Course
http://www.liv.ac.uk/HPC/HTMLFrontPageF90.html
Program Memory
(organised
into a series of
pigeonholes)
CPU
Files, keyboard,
display etc.
Algorithm:
1. Multiply the hours by 60
2. Add the minutes to the result
3. Multiply the result by 60
4. Add the seconds to the result
Nick Maclaren
Computing Service
November 2007
Introduction to Programming with Fortran 90 – p. 1/??
Coverage
This course is modern, free-format source only
The same applies to features covered later
For example:
UPPER case for keywords, lower for names
You may prefer Capitalised names
Nick Maclaren
Computing Service
November 2007
Introduction to Programming with Fortran 90 – p. 1/??
Data Types (1)
• INTEGER for exact whole numbers
e.g. 1, 100, 534, -18, -654321 etc.
In maths, an approximation to the ring Z
For example:
Sum = Term1 + Term2 + (Eps * Err)
Max = 2 * Min
INTEGER :: K, L, M
N = K*(L+2)/M**3-N
INTEGER :: K = 5
REAL :: X = 1.3
X = X+K/2
INTEGER :: K = 1000000
REAL :: X = 1.0e20
PRINT *, (K*K)/K, (X*X)/X
For example:
Y = SQRT(X)
PI = 4.0 * ATAN(1.0)
Z = EXP(3.0*Y)
X = REAL(N)
N = INT(X)
Y = SQRT(-2.0*LOG(X))
IF (red) THEN
PRINT *, ’Stop’
red = .False. ; amber = .True. ; green = .False.
ELSIF (red .AND. amber) THEN
. . .
CHARACTER(LEN=6) :: identity, A, B, Z
identity = ’TH’ // ’OMAS’
A = ’TH’ ; B = ’OMAS’
Z = A // B
PROGRAM message
IMPLICIT NONE
CHARACTER :: mess*72, date*14, name*40
mess = ’Program run on’
mess(30:) = ’by’
READ *, date, name
mess(16:29) = date
mess(33:) = name
PRINT *, mess
ENDPROGRAM message
CHARACTER(LEN=6) :: A, B, Z
A = ’TH’ ; B = ’OMAS’
Z = TRIM(A) // B
’A’ < ’B’ < ... < ’Y’ < ’Z’ | These ranges
’a’ < ’b’ < ... < ’y’ < ’z’ | will not
’0’ < ’1’ < ... < ’8’ < ’9’ | overlap
’ ’ is less than all of ’A’, ’a’ and ’0’
CHARACTER(LEN=maxlen) :: string
circum = pi * diam
IF (nchars < maxlen) THEN
...
PROGRAM name
Declarations
Other statements
END PROGRAM name
Nick Maclaren
Computing Service
November 2007
Introduction to Programming with Fortran 90 – p. 1/??
Control Constructs
These change the sequential execution order
We cover the main constructs in some detail
We shall cover procedure call later
IF (MOD(count,1000) == 0) &
PRINT *, ’Reached’, count
IF (X < A) X = A
yes: DO
PRINT *, ’y’
ENDDO yes
DO I = 3 , 1
PRINT *, 7*I-3
ENDDO
Does nothing
DO I = 20 , 1 , 7
PRINT *, I
ENDDO
Does nothing
DO I = 1 , 20 , -7
PRINT *, I
ENDDO
Does nothing
DO
x = read_number()
IF (x < 0.0) EXIT
count = count+1; total = total+x
IF (x == 0.0) CYCLE
...
ENDDO
DO
IF (.NOT. ( <logical expression> )) EXIT
Nick Maclaren
Computing Service
November 2007
Introduction to Programming with Fortran 90 – p. 1/??
Array Declarations
Fortran is the array-handling language
Applications like Matlab descend from it
For example:
arr1(1:63) = 5 ; arr1(64:100) = 7
arr2 = arr1(1:50)+arr3(51:100)
arr1(26:75) = arr1(1:50)+arr1(51:100)
A(1:3,1:4)
A(2:5,7)
INTEGER :: NA
REAL, DIMENSION(1:100) :: A
READ *, NA, A(1:NA)
INTEGER :: marks(1:6)
marks = (/ 10, 25, 32, 54, 54, 60 /)
But NOT:
REAL :: arr(1:3) = (/ 1.0, exp(1.0), exp(2.0) /)
ALLOCATE (counts(1:1000000))
ALLOCATE (value(0:N, -5:5, M:2*N+1))
j−1
X
∀i>j , Lij = (Aij − Lik Ljk )/Ljj
k=1
Nick Maclaren
Computing Service
November 2007
Introduction to Programming with Fortran 90 – p. 1/??
Sub-Dividing The Problem
• Most programs are thousands of lines
Few people can grasp all the details
PROGRAM MAIN
IMPLICIT NONE
REAL, DIMENSION(5, 5) :: A = 0.0
REAL, DIMENSION(5) :: Z
...
CALL CHOLESKY (A)
...
END PROGRAM MAIN
PROGRAM sort10
IMPLICIT NONE
INTEGER, DIMENSION(1:10) :: nums
...
! --- Sort the numbers into ascending order of magnitude
CALL SORTIT (nums, 10)
! --- Write out the sorted list
...
END PROGRAM sort10
CALL Munge(1.23)
MODULE mymod
CONTAINS
FUNCTION Variance (Array)
REAL, INTENT(IN), DIMENSION(:) :: Array
X = SUM(Array)/SIZE(Array)
Variance = SUM((Array-X)**2)/SIZE(Array)
END FUNCTION Variance
END MODULE mymod
PROGRAM main
USE mymod
REAL, DIMENSION(10) :: array
PRINT *, ’Type 10 values’
READ *, array
PRINT *, ’Variance = ’, Variance(array)
END PROGRAM main
INTEGER :: X
REAL, DIMENSION(100) :: Y, Z
CALL Nuts (Y=X, Z=1, X=Y)
INTEGER, PARAMETER :: M = 5, N = 10
REAL, DIMENSION(1:M, 1:N) :: table
REAL, DIMENSION(1000) :: workspace
CALL Orace(table, workspace)
CHARACTER(LEN=125) :: buffer
CALL grockle(buffer, 125)
Nick Maclaren
Computing Service
November 2007
Introduction to Programming with Fortran 90 – p. 1/??
Warning: Time Warp
Unfortunately, we need to define a module
We shall cover those quite a lot later
E.g. SELECTED_REAL_KIND(12)
at least 12 decimal places
MODULE double
INTEGER, PARAMETER :: DP = KIND(0.0D0)
END MODULE double
USE double
IMPLICIT NONE
REAL(KIND=DP) :: a, b, c
REAL(KIND=DP), DIMENSION(10) :: x, y, z
MODULE double
INTEGER, PARAMETER :: DP = &
SELECTED_REAL_KIND(12, 100)
END MODULE double
REAL(KIND=KIND(0.0D0)) :: a, b, c
REAL(KIND=DP) :: x
x = REAL(<integer expression>, KIND=DP)
x = <integer expression>
x = DBLE(<integer expression>)
COMPLEX(KIND=DP) :: c
c = (1.23_DP,4.56_DP)
c = (1.23_DP,4.56_DP)*CONJG(d)+SIN(f*g)
e = EXP(d+c/f)*ABS(LOG(e))
c = CMPLX(<X-expr>, KIND=DP)
c = CMPLX(<X-expr>, <Y-expr>, KIND=DP)
c = CMPLX(1.0_DP,2.0_DP)
COMPLEX(KIND=DP) :: c = (1.23_DP,4.56_DP)
WRITE (*, *) C
Prints “(1.23,4.56)”
And similarly for input
Nick Maclaren
Computing Service
November 2007
Introduction to Programming with Fortran 90 – p. 1/??
Module Summary
• Similar to same term in other languages
As usual, modules fulfil multiple purposes
MODULE double
IMPLICIT NONE
INTEGER, PARAMETER :: DP = KIND(0.0D0)
END MODULE double
MODULE parameters
USE double
IMPLICIT NONE
REAL(KIND=DP), PARAMETER :: one = 1.0_DP
END MODULE parameters
MODULE parameters
USE double
REAL(KIND=DP), PARAMETER :: one = 1.0_DP
INTEGER, PARAMETER :: NX = 10, NY = 20
END MODULE parameters
MODULE workspace
USE double ; USE parameters
REAL(KIND=DP), DIMENSION(NX, NY) :: now, then
END MODULE workspace
Introduction to Programming with Fortran 90 – p. 8/??
Example (2)
The main program might use them like this
PROGRAM main
USE double
USE parameters
USE workspace
...
END PROGRAM main
double
parameters
workspace
Main
Program
Introduction to Programming with Fortran 90 – p. 10/??
Module Dependencies
Program
MODULE double
INTEGER, PARAMETER :: DP = KIND(0.0D0)
END MODULE double
MODULE Bicycle
REAL, PARAMETER :: pi = 3.141592
TYPE Wheel
INTEGER :: spokes
REAL :: diameter, width
CHARACTER(LEN=15) :: material
ENDTYPE Wheel
END MODULE Bicycle
USE Bicycle
TYPE(Wheel) :: w1
USE state_variables
IMPLICIT NONE
DO
current = current + increment
CALL next_step(current, values)
ENDDO
MODULE state_variables
IMPLICIT NONE
SAVE
INTEGER, PARAMETER :: nx=100, ny=100
REAL, DIMENSION(NX, NY) :: &
current, increment, values
REAL :: time = 0.0
ENDMODULE state_variables
MODULE state_variables
REAL, DIMENSION(:, :), ALLOCATABLE, &
SAVE :: current, increment, values
ENDMODULE state_variables
USE state_variables
IMPLICIT NONE
INTEGER :: NX, NY
READ *, NX, NY
ALLOCATE (current(NX, NY), increment(NX, NY), &
values(NX, NY))
SUBROUTINE joe
USE status
state = 0.0
END SUBROUTINE joe
CALL joe
PRINT *, state ! this is UNDEFINED
CALL alf(state)
PRINT *, state ! this is defined to be 0.0
PROGRAM main
USE mymod
...
PRINT *, ’Variance = ’, Variance(array)
55.0000000
2.2500000E+02
MODULE fred
REAL, PRIVATE :: array(100)
REAL, PUBLIC :: total
INTEGER, PRIVATE :: error_count
CHARACTER(LEN=50), PUBLIC :: excuse
CONTAINS
...
END MODULE fred
MODULE fred
PRIVATE
REAL :: array(100)
REAL, PUBLIC :: total
CONTAINS
...
END MODULE fred
MODULE workspace
USE double
PRIVATE :: DP
REAL(KIND=DP), DIMENSION(1000) :: scratch
END MODULE workspace
MODULE workspace
USE settings
REAL(KIND=DP), DIMENSION(1000) :: scratch
END MODULE workspace
• DP is inherited, which is OK
MODULE workspace
USE settings, ONLY : DP
REAL(KIND=DP), DIMENSION(1000) :: scratch
END MODULE workspace
MODULE workspace
USE settings, ONLY : DP
PRIVATE :: DP
REAL(KIND=DP), DIMENSION(1000) :: scratch
END MODULE workspace
PROGRAM house
USE corner, sanders => pooh
INTEGER, DIMENSION(20) :: pooh
...
END PROGRAM house
MODULE two
USE one, Y => X
REAL :: Z
END MODULE two
PROGRAM three
USE one ; USE two
! Both X and Y refer to the same variable
END PROGRAM three
Nick Maclaren
Computing Service
November 2007
Introduction to Programming with Fortran 90 – p. 1/??
Summary
There is one important new feature to cover
TYPE(Wheel) :: w1
TYPE Bicycle
CHARACTER(LEN=80) :: description(100)
TYPE(Wheel) :: front, back
REAL, ALLOCATABLE, DIMENSION(:) :: times
INTEGER, DIMENSION(100) :: codes
ENDTYPE Bicycle
And so on ...
TYPE(Bicycle) :: mine
TYPE :: Rabbit
CHARACTER(LEN=16) :: variety
REAL :: weight, length
INTEGER :: age
ENDTYPE Rabbit
TYPE(Rabbit), DIMENSION(100) :: exhibits
REAL, DIMENSION(50) :: fattest
fattest = exhibits(51:)%weight
yours = mine
mine%front = yours%back
TYPE Fred
REAL :: x
END TYPE Fred
TYPE Joe
REAL :: x
END TYPE Joe
TYPE(Fred) :: a
TYPE(Joe) :: b
a = b ! This is erroneous
TYPE(Circle) :: a
a = Circle(1.23, 4.56, 2.0, .False.)
TYPE Circle
REAL :: X = 0.0, Y = 0.0, radius = 1.0
LOGICAL :: filled = .False.
END TYPE Circle
TYPE(Circle) :: a, b, c
a = Circle(1.23, 4.56, 2.0, .True.)
TYPE(Circle) :: a, b, c
a = Circle(1.23, 4.56, 2.0, .True.)
PRINT *, a ; PRINT *, b ; PRINT *, c
Nick Maclaren
Computing Service
November 2007
Introduction to Programming with Fortran 90 – p. 1/??
I/O Generally
Most descriptions of I/O are only half-truths
Those work most of the time – until they blow up
Most modern language standards are like that
† CC = committee compromise
INTEGER, DIMENSION(20) :: N
CHARACTER(LEN=50) :: C
• It is unlike anything in C
DO k = 1,...
WRITE (1) k, m, n, index(:m), array(:n)
ENDDO
• A CHARACTER expression
• A CHARACTER array
Concatenated in array element order
‘ ( i3,f 5 . 2) ’ ≡ ‘(i3,f5.2)’
123 -123
00123 -00123
1111011 0001111011
173 00173
7B 0007B
Prints
/12345/987.65
Will display:
a
abc
With input:
abcdefgh
a
Will display:
fgh
a
7 9
Produces ‘7 9 !’
7
9
1111
2222
3333
4444
Produces “1 2 4”
Hello
Goodbye
123 cubits
42
1.23 4.56
INTEGER :: ioerr
• End-of-file
Question 1
----------
PROGRAM INVERT
IMPLICIT NONE
REAL :: Value, Inverse
PRINT *, "Type in a value to invert"
READ *, Value
Inverse = 1.0/Value
PRINT *, "Value", Value, " Inverse", Inverse
END PROGRAM INVERT
1.1 Using an editor, type the above program into a file called something
like `invert.f90'.
1.2 Compile and run the program. Verify the correctness of the code by
supplying the following test data (each value needs a separate run):
1.4 See what happens when you supply the following data (each value
needs a separate run):
1.5 Edit the file, change the occurrence of `Value' to `Vole' in the
`READ' statement, and see what happens when you compile it.
1.6 Restore the file from the safe copy, change the `READ' to `RODE',
and see what happens when you compile it.
1.7 Restore the file from the safe copy, change the first `INVERT' to
`invert', the first `Value' to `valUE', and the `Type' to `TYPE'. Now
see what happens when you compile and run it with the input `1.0'.
Practical Exercise 2
--------------------
Question 1
----------
Question 2
----------
When IMPLICIT NONE is used at the start of a program all variable names
beginning with I, J, K, L, M, N are assumed to be INTEGER. Is this true
or false?
Question 3
----------
PROGRAM MAIN;INTEGER::degreesfahrenheit&
,degreescentigrade;READ*,&
degreesfahrenheit;degreescentigrade&
=5*(degreesfahrenheit-32)/9;PRINT*,&
degreesCENtiGrAde;END
Practical Exercise 3
--------------------
Question 1
----------
A/B*C
A*B/C
3*A**B
B+A/C
A/B+C
1.3 Write a program to check that your Fortran compiler gives the
results you have given.
Question 2
----------
2.1 What value does the real variable A take in the following statements
when I = 5 and J = 4 (I and J are INTEGER)?
A = I*J
A = I/J
A = J/I
2.2 What value does the INTEGER I take in the following statements when
A = 5.0, B = 4.0 (A and B are REAL)?
I = A*B
I = A/B
I = B/A
Question 3
----------
3.1 What value does the expression (A/B) have when A = 1.0, B = 0.0?
3.2 What value does the expression (5.3E+25 * 6.4E+28) have?
3.3 What value does the expression (5.3E-50 * 6.4E-35) have?
Question 4
----------
Question 5
----------
REAL :: pi = 22.0/3.0
Question 6
----------
Question 7
----------
INTEGER :: ONE = 1
ONE = 0
Question 8
----------
Which of the following are incorrect declarations and why? If you think
a declaration may be correct in a given situation then say what the
situation would be.
8.1 ReAl :: x
8.2 CHARACTER :: name
8.3 CHARACTER(LEN=10) :: name
8.4 REAL :: var-1
8.5 INTEGER :: 1a
8.6 INTEGRAL :: loji
8.7 CHARACTER(LEN=5) :: town = "Glasgow"
8.8 CHARACTER(LEN=*), PARAMETER :: city = "Glasgow"
8.9 INTEGER :: pi = +22/7
8.10 CHARACTER(LEN=*), PARAMETER :: "Bognor"
8.11 REAL, PARAMETER :: pye = 22.0/7.0
8.12 REAL, PARAMETER :: two_pie = pye*2
8.13 REAL :: a = 1., b = 2
8.14 CHARACTER(LEN=6) :: you_know = 'y'know"
8.15 CHARACTER(LEN=6) :: you_know = "y'know"
8.16 INTEGER :: ia ib ic id
8.17 REAL :: poie = 4.*atan(1.)
Question 9
----------
Question 10
-----------
-a*b-c/d**e/f+g**h+1-j/k
Question 11
-----------
11.1 Write a program that reads in a person's name and date of birth (in
the form `dd.mm.yy') and prints out a sentence such as:
Question 12
-----------
Write a simple program to read in a radius and calculate the area of the
corresponding circle and volume of the corresponding sphere.
Demonstrate its correctness by calculating the area and volume using
radii of 2, 5, 10 and -1.
The area of a circle is pi times the radius squared, and pi may be taken
as 3.14159. The volume of a sphere is 4/3 pi times the radius cubed.
Practical Exercise 4
--------------------
Question 1
----------
How many times are the loops, defined by the following statements,
executed:
DO I = 2, N/2, 3
DO I = 1, N, 4
DO I= 3, N**2, 5
when N is
1.1 2
1.2 15
Question 2
----------
length angle
2.1 12 77
2.2 1000 0
2.3 1000 90
2.4 20 100
2.5 12 437
Question 3
----------
Question 4
----------
4.1 1 1 1
4.2 2 2 1
4.3 1 1 0
4.4 3 4 5
4.5 3 2 1
4.6 1 2 4
Hint: if three lengths form a triangle then 2 times the longest side
must be less than the sum of all three sides. In Fortran 90 terms, the
following must be true:
Question 5
----------
5.1 7
0
5.2 106
46
3
0
Question 6
----------
Practical Exercise 5
--------------------
Give the rank, bounds, size and shape of the arrays defined as follows:
Question 2: Conformance
-----------------------
Given:
3.2 How would the 2nd bed in the 5th room on floor 7 be referenced?
Given:
INTEGER :: i = 3, j = 7
REAL, DIMENSION(1:20) :: A
which of the following are valid array references for the array:
4.1 A(12)
4.2 A(21)
4.3 A(I*J-1)
4.4 A(3.0)
4.5 A(I*J)
4.6 A(1+INT(4.0*ATAN(1.0)))
[ Hint: 4.0*ATAN(1.0) is pi ]
Given:
Write an array constructor for the rank one 5 element array BOXES
8.2.1 A = MATMUL(P,T)
8.2.2 P = MATMUL(Q,R)
8.2.3 A = P*T
8.2.4 P = Q*R
8.2.5 T = MATMUL(T,A)
In this question, you may assume that there are no errors and omit all
checking. That is not good practice, but considerably simplifies the
coding. You should not assume the matrices are square.
9.1 Replace the ". . ." by the statements needed to calculate the matrix
(not elementwise) product `result = left*right' using basic scalar
operations only.
9.2 Replace the ". . ." by the statements needed to calculate the matrix
Practical Exercise 6
--------------------
1.1 Write a main program and internal subroutine that takes three REAL
arguments and returns, as its first argument, the third argument
subtracted from the second. Keep the code as simple as possible. Test
this by including calls to add the following pairs of numbers:
1.23 4.56
-7.89 3.42
4.29 -0.98
1.2 Add INTENT to the above code and check that it works.
1.3 Change the call to using keywords and change the order of the
arguments; check that this does not change the results. Save this
code in a file.
2.1 Write a main program and an internal function that takes two REAL
arguments and returns the second argument subtracted from the first.
Keep the code as simple as possible. Test this by including calls to add
the following pairs of numbers:
1.23 4.56
-7.89 3.42
4.29 -0.98
2.2 Add INTENT and ELEMENTAL to the above code and check that it works.
2.3 Change the call to using keywords and reverse the order of the
arguments; check that this does not change the results.
REAL :: Z
CALL RANDOM_NUMBER(Z)
3.2 Write a main program that uses that module, and prints out the
result of the throw of two dice 10 times. Compile and run it.
Question 4: Arrays
------------------
4.1 Write a program with an internal subroutine that takes two assumed
shape 2-dimensional REAL array arguments, with INTENT(OUT) and
INTENT(IN), checks that their shapes are compatible, and allocates two
1-dimensional REAL arrays the same size as the first and second
dimensions.
4.2 Add the code to set the two automatic arrays to the row and column
averages, and shift the rows and columns so that all row and column
totals are zero. To do this, set the first argument to the value of the
second with the relevant row and column averages subtracted from it and
the overall average added to it. Test this by providing it with the
matrix:
Write a program with an internal subroutine that takes one INTEGER and
three assumed size CHARACTER arguments, prints out each of the CHARACTER
ones on a separate line, and sets the integer argument to the sum of
their lengths. Use the following data to test it:
Write a program with an internal function that takes one assumed size
CHARACTER argument, and returns the value argument with leading and
trailing spaces removed and padded to the same length as its argument
with '.'s. Test this with the following strings:
Write a subroutine with one INTEGER argument that returns 7 the first
time it is called, 8 the next time, 9 the next time, and so on. Declare
the argument INTENT(OUT). Test it however you prefer.
Question 8: Newton-Raphson
--------------------------
8.1 Write a module containing a subroutine and two functions; you can
put both of them after the module's CONTAINS at the same level (i.e. the
functions need not be internal procedures of the subroutine).
The subroutine should accept a value of X*LOG(X) greater than 1.0 and
print the corresponding value of X, by calling the functions and using
Newton-Raphson iteration.
The first function should return the value of X*LOG(X), where x is its
argument.
The second function should return the value of LOG(X)+1.0, where x is its
argument [LOG(X)+1.0 is the first derivative of X*LOG(X)].
8.2 Write a program that calls that subroutine, and test it with the
following values:
Practical Exercise 7:
---------------------
Question 1:
-----------
MODULE double
INTEGER, PARAMETER :: DP = KIND(0.0D0)
END MODULE double
Compare the two sets of results, and note the much increased accuracy in
the second set. Inspect them and the source carefully, and check that
you understand why that is.
Question 2:
-----------
Question 3:
-----------
3.2 Fix the errors that are flagged by the compiler and try again. What
has gone wrong now, and why?
Question 4:
-----------
Practical Exercise 08
---------------------
Question 1
----------
1.2 Write a main program that uses the module, reads in an integer,
checks that it is positive and no greater than `maxlength', allocates
`totals' to be of that length, and print out the exception status and
`message'. Check that the program compiles and runs (it doesn't do
anything useful).
Question 2
----------
and contains:
2.2 Write a main program that uses that module and calls `A', `B',
`A', `B',`A', `A', `C' and `D'. Compile and test it.
2.3 Change the module to declare the variables PRIVATE, recompile it,
and recompile and test the main program.
2.4 Remove the PRIVATE, take out the code for `A', `B', `C' and `D' and
create four other modules, each containing one subroutine. When using the
original module, import only the variables that they need. Recompile all
modules.
2.5 Change the main program to import only the modules containing `A',
`B', `C' and `D' and not the one containing the variables. Compile and
test it. Note that this shows one way of sharing data between
procedures, but not exporting it to users of those procedures.
Question 3
----------
3.2 Take the rest of the file `Programs/CubeRoot.f90' (i.e. with the
functions removed) and produce an interface declaration for those two
functions. Now compile and link that, togther with the compiled code of
the functions, and test it.
Question 4
----------
4.2 Create a main program that contains just the main program from
`Programs/Cholesky.f90'and uses module `Lapack'. Compile and link that
and check that it works.
4.3 Remove the `USE double' from the main program, recompile it and test
it. What goes wrong and why?
4.4 Move the `USE double' in module `Lapack' from subroutine `CHOLESK4'
to immediately after the MODULE statement, recompile it and then
recompile and test the main program. Why does it work again?
4.5 Now put the `USE double' back into the main program (i.e. undo what
you did in 4.3), recompile it and test it. Check that it still works.
4.6 Now replace the `USE double' in module `Lapack' by its source form
Recompile it and then recompile and test the main program. What goes
wrong and why? And why did 4.5 work if this didn't?
Question 5
----------
MODULE Lapack
USE double
PRIVATE :: dp
and the main program does NOT have a `USE double'. Why does this not
work when 4.4 did?
5.2 Now put the `USE double' back into the main program. Check that it
5.3 Now replace the `USE double' in module `Lapack' by its source form
Recompile it and then recompile and test the main program. Why does
this work if 4.6 didn't?
Practical Exercise 09
---------------------
Question 1
----------
1.1 Write a module that defines a derived type My_complex containing two
INTEGERs (note), and contains a function to add two My_complex values
(as if they were complex numbers) and return the result.
Question 2
----------
'Leechford' 5
13.17 42.62 46.24 5.51 23.23
The first line is a quoted string and an integer, which gives the count
of the number of reals on the next line.
The subroutine takes a value of type Midden and prints its contents
(you need not quote the string). If the name and array are both
null, it prints a message saying so.
Question 3
----------
Add PRIVATE to hide the components of the derived type used in question
2 and check that the program and module still work. Do so to the one
used in question 1 and see what fails.
Question 1
----------
PROGRAM INVERT
IMPLICIT NONE
REAL :: Value, Inverse
PRINT *, "Type in a value to invert"
READ *, Value
Inverse = 1.0/Value
PRINT *, "Value", Value, " Inverse", Inverse
END PROGRAM INVERT
1.1 Using an editor, type the above program into a file called something
like `invert.f90'.
1.2 Compile and run the program. Verify the correctness of the code by
supplying the following test data (each value needs a separate run):
Example output:
1.4 See what happens when you supply the following data (each value
needs a separate run):
Example output:
1.5 Edit the file, change the occurrence of `Value' to `Vole' in the
`READ' statement, and see what happens when you compile it.
Example output:
detected at INVERT@<end-of-statement>
[f90 terminated - errors found by pass 1]
1.6 Restore the file from the safe copy, change the `READ' to `RODE',
and see what happens when you compile it.
Example output:
1.7 Restore the file from the safe copy, change the first `INVERT' to
`invert', the first `Value' to `valUE', and the `Type' to `TYPE'. Now
see what happens when you compile and run it with the input `1.0'.
Example output:
Question 1
----------
Answers
-------
1.7 `v1' and `V1' are valid, `1v' and `1V' are not valid (they start
with a number), `v_1' is valid, `_v1' is not valid (it must start with a
letter), `v%1' is not valid (all characters must be alphanumeric or
underscore).
Question 2
----------
When IMPLICIT NONE is used at the start of a program all variable names
beginning with I, J, K, L, M, N are assumed to be INTEGER. Is this true
or false?
Question 3
----------
PROGRAM MAIN;INTEGER::degreesfahrenheit&
,degreescentigrade;READ*,&
degreesfahrenheit;degreescentigrade&
=5*(degreesfahrenheit-32)/9;PRINT*,&
degreesCENtiGrAde;END
PROGRAM MAIN
IMPLICIT NONE
INTEGER :: degrees_fahrenheit, degrees_centigrade
READ *, degrees_fahrenheit
degrees_centigrade = 5*(degrees_fahrenheit-32)/9
PRINT *, degrees_centigrade
END
Question 1
----------
A/B*C
A*B/C
3*A**B
B+A/C
A/B+C
1.3 Write a program to check that your Fortran compiler gives the
results you have given.
Specimen Answer
---------------
PROGRAM Trivial
INTEGER :: A, B, C
A=6
B=2
C=3
PRINT *, A/B*C, A*B/C, 3*A**B, B+A/C, A/B+C
A=3
B=5
C=4
Question 2
----------
2.1 What value does the real variable A take in the following statements
when I = 5 and J = 4 (I and J are INTEGER)?
A = I*J
A = I/J
A = J/I
2.2 What value does the INTEGER I take in the following statements when
A = 5.0, B = 4.0 (A and B are REAL)?
I = A*B
I = A/B
I = B/A
Question 3
----------
3.1 What value does the expression (A/B) have when A = 1.0, B = 0.0?
3.2 What value does the expression (5.3E+25 * 6.4E+28) have?
3.3 What value does the expression (5.3E-50 * 6.4E-35) have?
Answers
-------
3.1 Overflow.
3.2 Overflow.
3.3 Underflow.
Question 4
----------
Question 5
----------
REAL :: pi = 22.0/3.0
Answer: The first defines a named constant pi with value 22.0/3.0, the
second declares a variable named pi initialised to 22.0/3.0.
Question 6
----------
Question 7
----------
INTEGER :: ONE = 1
ONE = 0
Question 8
----------
Which of the following are incorrect declarations and why? If you think
a declaration may be correct in a given situation then say what the
situation would be.
8.1 ReAl :: x
Correct, but quirky
8.2 CHARACTER :: name
Correct
8.3 CHARACTER(LEN=10) :: name
Correct
8.4 REAL :: var-1
INCORRECT - can't declare an expression
8.5 INTEGER :: 1a
INCORRECT - names must start with a letter
8.6 INTEGRAL :: loji
INCORRECT - no such statement
8.7 CHARACTER(LEN=5) :: town = "Glasgow"
Correct, but town will contain "Glasg"
8.8 CHARACTER(LEN=*), PARAMETER :: city = "Glasgow"
Correct
8.9 INTEGER :: pi = +22/7
Correct, but not useful - value of pi will be 3
8.10 CHARACTER(LEN=*), PARAMETER :: "Bognor"
INCORRECT - no constant name given
8.11 REAL, PARAMETER :: pye = 22.0/7.0
Correct
8.12 REAL, PARAMETER :: two_pie = pye*2
Correct, provided pye is defined previously
8.13 REAL :: a = 1., b = 2
Correct, but not good practice
Question 9
----------
Question 10
-----------
-a*b-c/d**e/f+g**h+1-j/k
Question 11
-----------
11.1 Write a program that reads in a person's name and date of birth (in
Specimen Answer
---------------
PROGRAM sample
IMPLICIT NONE
CHARACTER(LEN=40) :: name
CHARACTER(LEN=8) :: date
PRINT *, 'Type name'
READ *, name
PRINT *, 'Type date'
READ *, date
PRINT *, date // ' is the birthday of ' // trim(name)
END PROGRAM sample
Specimen Answer
---------------
PROGRAM sample
IMPLICIT NONE
CHARACTER(LEN=40) :: name
CHARACTER(LEN=8) :: date
PRINT *, 'Type name'
READ *, name
PRINT *, 'Type date'
READ *, date
PRINT *, trim(name) // ' was born on ' // date
END PROGRAM sample
Question 12
-----------
Write a simple program to read in a radius and calculate the area of the
corresponding circle and volume of the corresponding sphere.
The area of a circle is pi times the radius squared, and pi may be taken
as 3.14159. The volume of a sphere is 4/3 pi times the radius cubed.
Specimen Answer
---------------
PROGRAM Area
IMPLICIT NONE
REAL :: radius
PRINT *, "Type in the radius"
READ *, radius
PRINT *, "Area of circle with radius ", &
radius, " is ", 3.14159*radius**2
PRINT *, "Volume of sphere with radius ", &
radius, " is ", (4.0/3.0)*3.14159*radius**3
END PROGRAM Area
Question 1
----------
How many times are the loops, defined by the following statements,
executed:
DO I = 2, N/2, 3
DO I = 1, N, 4
DO I= 3, N**2, 5
when N is
1.1 2
1.2 15
Answers:
1.1 0, 1, 1
1.2 2, 4, 45
Question 2
----------
length angle
2.1 12 77
2.2 1000 0
2.3 1000 90
2.4 20 100
2.5 12 437
Specimen Answer
---------------
PROGRAM Cartesian
IMPLICIT NONE
REAL :: pi, length, angle, angle_in_radians
pi = ATAN(1.0)*4.0
PRINT *, "Type in length and angle (in degrees)"
READ *, length, angle
angle_in_radians = (angle/180.0)*pi
PRINT *, "(x, y) is (", length*COS(angle_in_radians), &
",", length*SIN(angle_in_radians), " )"
END PROGRAM Cartesian
Question 3
----------
Specimen Answer
---------------
PROGRAM Multiples_of_Five
IMPLICIT NONE
INTEGER :: i, num, N = 0
DO i = 1,10
READ *, num
IF (num < 0) CYCLE
IF (MOD(num,5) == 0) N = N+1 ! MOD is an intrinsic function
ENDDO
Question 4
----------
4.1 1 1 1
4.2 2 2 1
4.3 1 1 0
4.4 3 4 5
4.5 3 2 1
4.6 1 2 4
Hint: if three lengths form a triangle then 2 times the longest side
must be less than the sum of all three sides. In Fortran 90 terms, the
following must be true:
Specimen Answer
---------------
PROGRAM Triangle
IMPLICIT NONE
LOGICAL :: L1, L2
INTEGER :: side1, side2, side3
PRINT *, "Type in the three sides:"
READ *, side1, side2, side3
IF (2*MAX(side1, side2, side3) >= side1+side2+side3) THEN
PRINT *, "Not a Triangle"
ELSE
L1 = (side1 == side2)
L2 = (side2 == side3)
IF (L1 .AND. L2) THEN
PRINT *, "Equilateral"
4.1 1 1 1 Equilateral
4.2 2 2 1 Isoceles
4.3 1 1 0 Not a Triangle
4.4 3 4 5 Scalene
4.5 3 2 1 Not a Triangle [ a straight line ]
4.6 1 2 4 Not a Triangle
Question 5
----------
5.1 7
0
5.2 106
46
3
0
Specimen Answer
---------------
PROGRAM Math_Magic
IMPLICIT NONE
INTEGER :: num, tnum
outer: DO
PRINT *, "Type in your number (0 terminates)"
READ *, num
IF (num .LE. 0) EXIT
inner: DO
tnum = num/2
IF (2*tnum .EQ. num) THEN ! num is even
num = tnum
ELSE ! num is odd
num = 3*num+1
END IF
PRINT *, num
IF (num == 1) THEN
PRINT *, "Sequence finishes nicely"
EXIT
ELSE IF (num == 13) THEN
PRINT *, "Yoiks, extreme bad luck encountered"
EXIT outer
END IF
END DO inner
END DO outer
END PROGRAM Math_Magic
Question 6
----------
Specimen Answer
---------------
PROGRAM colours
IMPLICIT NONE
CHARACTER(LEN=10) :: colour
INTEGER :: nred = 0, ngreen = 0, nblue = 0, nyellow = 0, &
nblack = 0, nwhite = 0, nother = 0
DO
READ *, colour
colour = TRIM(ADJUSTL(colour)) ! gets rid of leading
! and trailing spaces
IF (colour == 'red') THEN
nred = nred+1
ELSEIF (colour == 'green') THEN
ngreen = ngreen + 1
ELSEIF (colour == 'blue') THEN
nblue = nblue + 1
ELSEIF (colour == 'yellow') THEN
nyellow = nyellow + 1
ELSEIF (colour == 'black') THEN
nblack = nblack + 1
ELSEIF (colour == 'white') THEN
nwhite = nwhite + 1
ELSEIF (colour == 'end') THEN
EXIT
ELSE
nother = nother+1
ENDIF
ENDDO
PRINT *, 'red =', nred, ' green =', ngreen, &
' blue =', nblue, ' yellow =', nyellow, &
' black =', nblack, ' white =', nwhite, &
' other =', nother
END PROGRAM colours
Give the rank, bounds, size and shape of the arrays defined as follows:
Question 2: Conformance
-----------------------
Given:
Answer: TWO and FOUR (note that they have the same rank, size and
shape, but their bounds differ)
3.2 How would the 2nd bed in the 5th room on floor 7 be referenced?
Answer:
3.2 Hotel(7, 5, 2)
Given:
INTEGER :: i = 3, j = 7
REAL, DIMENSION(1:20) :: A
which of the following are valid array references for the array:
4.1 A(12)
4.2 A(21)
4.3 A(I*J-1)
4.4 A(3.0)
4.5 A(I*J)
4.6 A(1+INT(4.0*ATAN(1.0)))
[ Hint: 4.0*ATAN(1.0) is pi ]
Answer:
4.1 Yes
4.2 No (out of bounds)
4.3 Yes - it is A(20)
4.4 No (wrong type of subscript)
4.5 No (out of bounds - I*J is 21)
4.6 Yes - it is A(4)
Given:
Answer:
Answer:
Write an array constructor for the rank one 5 element array BOXES
containing the values 1, 4, 6, 12, 23.
8.2.1 A = MATMUL(P,T)
8.2.2 P = MATMUL(Q,R)
8.2.3 A = P*T
8.2.4 P = Q*R
8.2.5 T = MATMUL(T,A)
Answer:
8.2.1 Yes
8.2.2 No
8.2.3 No
8.2.4 Yes
8.2.5 Yes
In this question, you may assume that there are no errors and omit all
checking. That is not good practice, but considerably simplifies the
coding. You should not assume the matrices are square.
9.1 Replace the ". . ." by the statements needed to calculate the matrix
(not elementwise) product `result = left*right' using basic scalar
operations only.
9.2 Replace the ". . ." by the statements needed to calculate the matrix
(not elementwise) product result = left*right using the SUM intrinsic.
Specimen Answer:
----------------
9.1
9.2
DO J = 1,UBOUND(result,1)
result(J,K) = SUM(left(J,:)*right(:,K))
ENDDO
ENDDO
END SUBROUTINE MyMatmul
Specimen Answer:
----------------
10.1 Code:
10.2 Code:
Specimen Answer:
----------------
DO I = 1, UBOUND(ARG, 1)
ARG(I,I) = 1.0
ENDDO
ELSE
TEMP = ARG
DO I = 1, N-1
ARG = MATMUL(ARG,TEMP)
ENDDO
ENDIF
END SUBROUTINE OPERATE
1.1 Write a main program and internal subroutine that takes three REAL
arguments and returns, as its first argument, the third argument
subtracted from the second. Keep the code as simple as possible. Test
this by including calls to add the following pairs of numbers:
1.23 4.56
-7.89 3.42
4.29 -0.98
1.2 Add INTENT to the above code and check that it works.
1.3 Change the call to using keywords and change the order of the
arguments; check that this does not change the results. Save this
code in a file.
Specimen Answer
---------------
1.1 Code:
PROGRAM main
IMPLICIT NONE
REAL :: X
CONTAINS
Results:
-3.3299999
-11.3099995
5.2700000
1.3 The executable statements of the main program become something like:
2.1 Write a main program and an internal function that takes two REAL
arguments and returns the second argument subtracted from the first.
Keep the code as simple as possible. Test this by including calls to add
the following pairs of numbers:
1.23 4.56
-7.89 3.42
4.29 -0.98
2.2 Add INTENT and ELEMENTAL to the above code and check that it works.
2.3 Change the call to using keywords and reverse the order of the
arguments; check that this does not change the results.
Specimen Answer
---------------
2.1 Code:
PROGRAM main
IMPLICIT NONE
REAL :: X
X = diff(1.23, 4.56)
PRINT *, X
X = diff(-7.89, 3.42)
PRINT *, X
X = diff(4.29, -0.98)
PRINT *, X
CONTAINS
Results:
-3.3299999
-11.3099995
5.2700000
IMPLICIT NONE
REAL :: diff
REAL, INTENT(IN) :: A, B
diff = A-B
END FUNCTION diff
2.3 The executable statements of the main program become something like:
X = diff(B=4.56, A=1.23)
PRINT *, X
X = diff(B=3.42, A=-7.89)
PRINT *, X
X = diff(B=-0.98, A=4.29)
PRINT *, X
REAL :: Z
CALL RANDOM_NUMBER(Z)
3.2 Write a main program that uses that module, and prints out the
result of the throw of two dice 10 times. Compile and run it.
Specimen Answer
---------------
3.1 Code:
MODULE rollem
CONTAINS
FUNCTION twodice ()
IMPLICIT NONE
INTEGER :: twodice
REAL :: temp
CALL RANDOM_NUMBER(temp)
twodice = INT(6.0*temp+1.0)
CALL RANDOM_NUMBER(temp)
twodice = twodice+INT(6.0*temp+1.0)
END FUNCTION twodice
END MODULE rollem
3.2 Code:
PROGRAM throw
USE rollem
IMPLICIT NONE
INTEGER :: k, n
DO k = 1,10
n = twodice()
PRINT *, n
ENDDO
END PROGRAM throw
Results:
These will change for each run, and all be in the range 2-12 inclusive.
2 and 12 should be rare, and 3 and 11 not frequent.
Question 4: Arrays
------------------
4.1 Write a program with an internal subroutine that takes two assumed
shape 2-dimensional REAL array arguments, with INTENT(OUT) and
INTENT(IN), checks that their shapes are compatible, and allocates two
1-dimensional REAL arrays the same size as the first and second
dimensions.
4.2 Add the code to set the two automatic arrays to the row and column
averages, and shift the rows and columns so that all row and column
totals are zero. To do this, set the first argument to the value of the
second with the relevant row and column averages subtracted from it and
the overall average added to it. Test this by providing it with the
matrix:
Specimen Answer
---------------
4.1 Code:
PROGRAM array
CONTAINS
m = UBOUND(arg1, 1)
n = UBOUND(arg1, 2)
IF (m /= UBOUND(arg2, 1) .OR. n /= UBOUND(arg2, 2)) THEN
PRINT *, 'The arrays are incompatible'
STOP
ENDIF
ALLOCATE(temp1(1:UBOUND(arg1, 1)))
ALLOCATE(temp2(1:UBOUND(arg1, 2)))
END SUBROUTINE scaler
4.2 Code:
PROGRAM array
IMPLICIT NONE
REAL, DIMENSION(3, 4) :: work1, work2
CONTAINS
m = UBOUND(arg1, 1)
n = UBOUND(arg1, 2)
IF (m /= UBOUND(arg2, 1) .OR. n /= UBOUND(arg2, 2)) THEN
PRINT *, 'The arrays are incompatible'
STOP
ENDIF
ALLOCATE(temp1(1:UBOUND(arg1, 1)))
ALLOCATE(temp2(1:UBOUND(arg1, 2)))
DO j = 1, m
temp1(j) = sum(arg2(j, :))/n
ENDDO
DO k = 1, n
temp2(k) = sum(arg2(:, k))/m
ENDDO
DO j = 1, n
DO k = 1, m
arg1(k, j) = arg2(k, j) - temp1(k) - &
temp2(j) + sum(temp1)/m
ENDDO
ENDDO
END SUBROUTINE scaler
Results:
Write a program with an internal subroutine that takes one INTEGER and
three assumed size CHARACTER arguments, prints out each of the CHARACTER
ones on a separate line, and sets the integer argument to the sum of
their lengths. Use the following data to test it:
Specimen Answer
---------------
PROGRAM trivial
IMPLICIT NONE
INTEGER :: n
CONTAINS
PRINT *, str1
PRINT *, str2
PRINT *, str3
count = LEN(str1)+LEN(str2)+LEN(str3)
END SUBROUTINE chars
Results:
Kilroy
was
here
13
Write a program with an internal function that takes one assumed size
CHARACTER argument, and returns the value argument with leading and
trailing spaces removed and padded to the same length as its argument
with '.'s. Test this with the following strings:
Specimen Answer
---------------
PROGRAM trivial
IMPLICIT NONE
CONTAINS
Results:
Write a subroutine with one INTEGER argument that returns 7 the first
time it is called, 8 the next time, 9 the next time, and so on. Declare
the argument INTENT(OUT). Test it however you prefer.
Specimen Answer
---------------
count = state
state = state+1
END SUBROUTINE counter
Question 8: Newton-Raphson
--------------------------
8.1 Write a module containing a subroutine and two functions; you can
put both of them after the module's CONTAINS at the same level (i.e. the
functions need not be internal procedures of the subroutine).
The subroutine should accept a value of X*LOG(X) greater than 1.0 and
print the corresponding value of X, by calling the functions and using
Newton-Raphson iteration.
The first function should return the value of X*LOG(X), where x is its
argument.
The second function should return the value of LOG(X)+1.0, where x is its
argument [LOG(X)+1.0 is the first derivative of X*LOG(X)].
8.2 Write a program that calls that subroutine, and test it with the
following values:
Specimen Answer
---------------
Module:
MODULE Solver
CONTAINS
Fn1 = arg*LOG(arg)
Fn2 = LOG(arg)+1.0
END FUNCTION Fn2
Program:
PROGRAM Tester
USE Solver
IMPLICIT NONE
CALL Newton(1.23)
CALL Newton(456.7)
CALL Newton(8.9e5)
END PROGRAM Tester
Results:
Question 1:
-----------
MODULE double
INTEGER, PARAMETER :: DP = KIND(0.0D0)
END MODULE double
Compare the two sets of results, and note the much increased accuracy in
the second set. Inspect them and the source carefully, and check that
you understand why that is.
Answer:
3.14159265358979323846 3.14159265358979323846
314159.265358979323846 314159.265358979323846
3.14159274101257324219 3.14159265358979311600
314159.250000000000000 314159.265358979348093
314159.281250000000000 314159.265358979289886
3.14159274101257324219 3.14159265358979311600
314159.250000000000000 314159.265358979348093
314159.281250000000000 314159.265358979289886
3.14159274101257324219 3.14159265358979311600
314159.250000000000000 314159.265358979348093
314159.281250000000000 314159.265358979289886
3.14159274101257324219 3.14159265358979311600
3.14159274101257324219 3.14159265358979311600
314159.281250000000000 314159.265358979289886
3.14159274101257324219 3.14159265358979311600
314159.250000000000000 314159.265358979348093
3.14159274101257324219 3.14159265358979311600
3.14159274101257324219 3.14159265358979311600
3.14159464836120605469 3.14159265358979622462
314159.375000000000000 314159.265358979522716
3.14138388633728027344 3.14159264457621567601
Question 2:
-----------
Answer:
Do you really need one? Yes, it really is that easy to use complex
arithmetic in Fortran!
You are recommended to experiment with formatted I/O and using built-in
Question 3:
-----------
3.2 Fix the errors that are flagged by the compiler and try again. What
has gone wrong now, and why?
Answer:
2. Most (but not all) results are either the same as the single
precision ones or of comparable accuracy. This is mostly because the
constants have been translated to single precision before being stored
by the compiler, and promoted to double precision when they are used.
However, the calls to `atan' with a constant argument have calculated it
in single precision, because it is a generic function and uses the type
of its argument to select its precision. This is why it is safest to
convert all constants, even ones that can be represented exactly in
single precision.
Question 4:
-----------
Question 1
----------
1.2 Write a main program that uses the module, reads in an integer,
checks that it is positive and no greater than `maxlength', allocates
`totals' to be of that length, and print out the exception status and
`message'. Check that the program compiles and runs (it doesn't do
anything useful).
Specimen Answer
---------------
1.1 Code:
MODULE Junk
IMPLICIT NONE
INTEGER, PARAMETER :: maxlength = 100
CHARACTER(LEN=*),PARAMETER :: message = 'Kilroy was here'
INTEGER, SAVE :: errors = 0
REAL, ALLOCATABLE, DIMENSION(:) :: totals
END MODULE Junk
1.2 Code:
PROGRAM Futile
USE JUNK
IMPLICIT NONE
INTEGER :: size, err
Print *, 'Type an integer'
READ *, size
IF (size <= 0 .OR. size > maxlength) STOP
ALLOCATE(totals(size), STAT=err)
PRINT *, err
PRINT *, message
END PROGRAM Futile
Results:
0
Kilroy was here
Question 2
----------
and contains:
2.2 Write a main program that uses that module and calls `A', `B',
`A', `B',`A', `A', `C' and `D'. Compile and test it.
2.3 Change the module to declare the variables PRIVATE, recompile it,
and recompile and test the main program.
2.4 Remove the PRIVATE, take out the code for `A', `B', `C' and `D' and
create four other modules, each containing one subroutine. When using the
original module, import only the variables that they need. Recompile all
modules.
2.5 Change the main program to import only the modules containing `A',
`B', `C' and `D' and not the one containing the variables. Compile and
test it. Note that this shows one way of sharing data between
procedures, but not exporting it to users of those procedures.
Specimen Answer
---------------
2.1 Code:
MODULE Summer
IMPLICIT NONE
REAL, SAVE :: one = 0.0, two = 0.0, three = 0.0
CONTAINS
SUBROUTINE A
IMPLICIT NONE
one = one+1.23
END SUBROUTINE A
SUBROUTINE B
IMPLICIT NONE
two = one+0.7
END SUBROUTINE B
SUBROUTINE C
IMPLICIT NONE
three = one+two
END SUBROUTINE C
SUBROUTINE D
IMPLICIT NONE
PRINT *, three
END SUBROUTINE D
END MODULE Summer
2.2 Code:
PROGRAM Main
USE Summer
IMPLICIT NONE
CALL A
CALL B
CALL A
CALL B
CALL A
CALL A
CALL C
CALL D
END PROGRAM Main
Results:
8.0799999
PRIVATE
REAL, SAVE :: one = 0.0, two = 0.0, three = 0.0
PUBLIC :: a, b, c, d
2.4 Codes:
MODULE Summer
IMPLICIT NONE
REAL, SAVE :: one = 0.0, two = 0.0, three = 0.0
END MODULE Summer
MODULE Summer_A
CONTAINS
SUBROUTINE A
USE Summer, ONLY : one
IMPLICIT NONE
one = one+1.23
END SUBROUTINE A
END MODULE Summer_A
MODULE Summer_B
CONTAINS
SUBROUTINE B
USE Summer, ONLY : one, two
IMPLICIT NONE
two = one+0.7
END SUBROUTINE B
END MODULE Summer_B
MODULE Summer_C
CONTAINS
SUBROUTINE C
USE Summer, ONLY : one, two, three
IMPLICIT NONE
three = one+two
END SUBROUTINE C
END MODULE Summer_C
MODULE Summer_D
CONTAINS
SUBROUTINE D
USE Summer, ONLY : three
IMPLICIT NONE
PRINT *, three
END SUBROUTINE D
END MODULE Summer_D
2.5 Code:
PROGRAM Main
USE Summer_A
USE Summer_B
USE Summer_C
USE Summer_D
IMPLICIT NONE
CALL A
CALL B
CALL A
CALL B
CALL A
CALL A
CALL C
CALL D
END PROGRAM Main
Question 3
----------
3.2 Take the rest of the file `Programs/CubeRoot.f90' (i.e. with the
functions removed) and produce an interface declaration for those two
functions. Now compile and link that, togther with the compiled code of
the functions, and test it.
Specimen Answer
---------------
3.1 Code:
3.2 Code:
PROGRAM Newton
USE double
IMPLICIT NONE
INTERFACE
FUNCTION value (arg, targ)
USE double
IMPLICIT NONE
REAL(KIND=DP) :: value, arg, targ
END FUNCTION value
FUNCTION derivative (arg)
USE double
IMPLICIT NONE
REAL(KIND=DP) :: derivative, arg
END FUNCTION derivative
END INTERFACE
REAL(KIND=DP) :: target, current
REAL(KIND=DP), DIMENSION(5) :: previous
INTEGER :: i, k
mainloop: DO
PRINT *, 'Type in a real number'
READ (*, *, IOSTAT=k) target
IF (k < 0) THEN
STOP
ELSEIF (k > 0) THEN
PRINT *, 'Some sort of horrible I/O error'
STOP
ENDIF
IF (target .EQ. 0.0_DP) THEN
PRINT *, 'The cube root of zero is, er, zero'
CYCLE
END IF
!
! This is cheating, but the hardest part of Newton-Raphson solution of
! Nth roots is getting the starting value, and doing so properly would
! merely be confusing. So use a horrible hack to get an approximation.
!
current = 1.1*CMPLX(target)**0.3
DO i = 1,5
previous(i) = 0.0_DP
END DO
loop: DO
current = current - &
value(current,target)/derivative(current)
PRINT *, current
DO i = 1,5
if (current .EQ. previous(i)) EXIT loop
END DO
DO i = 1,4
previous(i+1) = previous(i)
END DO
previous(1) = current
END DO loop
Question 4
----------
4.2 Create a main program that contains just the main program from
`Programs/Cholesky.f90'and uses module `Lapack'. Compile and link that
and check that it works.
4.3 Remove the `USE double' from the main program, recompile it and test
it. What goes wrong and why?
4.4 Move the `USE double' in module `Lapack' from subroutine `CHOLESK4'
to immediately after the MODULE statement, recompile it and then
recompile and test the main program. Why does it work again?
4.5 Now put the `USE double' back into the main program (i.e. undo what
you did in 4.3), recompile it and test it. Check that it still works.
4.6 Now replace the `USE double' in module `Lapack' by its source form
Recompile it and then recompile and test the main program. What goes
wrong and why? And why did 4.5 work if this didn't?
Specimen Answer
---------------
4.1 Code:
MODULE Lapack
CONTAINS
SUBROUTINE CHOLESKY(A)
USE double
IMPLICIT NONE
INTEGER :: J, N
REAL(KIND=dp) :: A(:, :), X
N = UBOUND(A,1)
DO J = 1, N
X = SQRT(A(J,J)-DOT_PRODUCT(A(J, :J-1), A(J, :J-1)))
A(J,J) = X
IF (J < N) &
A(J+1:, J) = (A(J+1:, J) - &
MATMUL(A(J+1:, :J-1), A(J,: J-1))) / X
ENDDO
END SUBROUTINE CHOLESKY
4.2 Code:
PROGRAM MAIN
USE double
USE Lapack
IMPLICIT NONE
REAL(KIND=dp), DIMENSION(5, 5) :: A = 0.0
REAL(KIND=dp), DIMENSION(5) :: Z
INTEGER :: I, N
DO N = 1,10
CALL RANDOM_NUMBER(Z)
DO I = 1,5
A(:, I) = A(:, I) + Z*Z(I)
END DO
END DO
WRITE (*,'(5(1X,5F10.6/))') A
CALL CHOLESKY(A)
DO I = 1,5
A(:I-1,I) = 0.0
END DO
WRITE (*, '(5(1X,5F10.6/))') A
WRITE (*, '(5(1X,5F10.6/))') MATMUL(A, TRANSPOSE(A))
END PROGRAM MAIN
Results:
4.3 It objects that symbol DP has not been declared. This is because it
has not been declared in module `Lapack' with module scope.
4.6 It objects that symbol `dp' is found both in module `double' and in
module 'Lapack'. 4ou can't import two different symbols of the same
name, even if they are functionally identical.
The reason that 4.5 worked is that it is the same symbol. Importing
a name from a module doesn't create a new instance of a symbol, or
a new variable.
Question 5
----------
MODULE Lapack
USE double
PRIVATE :: dp
and the main program does NOT have a `USE double'. Why does this not
work when 4.4 did?
5.2 Now put the `USE double' back into the main program. Check that it
now works again. Why is this?
5.3 Now replace the `USE double' in module `Lapack' by its source form
Recompile it and then recompile and test the main program. Why does
this work if 4.6 didn't?
Answers:
5.1 Because `dp' is now PRIVATE, it is not exported from `Lapack' and
so not imported into the main program.
5.2 Because the main program now imports `dp' directly from module
`double' and not at all from module `Lapack'.
5.3 Because `dp' is now PRIVATE, it is not exported from `Lapack' and so
not imported into the main program. Care should be taken doing this
sort of thing, because the declarations of `dp' had better be
functionally identical or chaos will ensue.
Question 1
----------
1.1 Write a module that defines a derived type My_complex containing two
INTEGERs (note), and contains a function to add two My_complex values
(as if they were complex numbers) and return the result.
Specimen Answer
---------------
1.1 Code:
MODULE Demo
IMPLICIT NONE
TYPE My_complex
INTEGER :: real, imaginary
END TYPE My_complex
CONTAINS
add%real = X%real+Y%real
add%imaginary = X%imaginary+Y%imaginary
END FUNCTION add
1.2 Code:
PROGRAM Run
USE Demo
IMPLICIT NONE
TYPE(My_complex) :: one = My_complex(123, 456), &
two = My_complex(432, 876), three
three = one
WRITE (*, '("( ", I0, " , ", I0, " )")') three
END PROGRAM Run
Results:
( 123 , 456 )
1.3 Code:
PROGRAM Run
USE Demo
IMPLICIT NONE
TYPE(My_complex) :: one = My_complex(123, 456), &
two = My_complex(432, 876), three, four
three = one
four%imaginary = two%real
four%real = two%imaginary
WRITE (*, '("( ", I0, " , ", I0, " )")') add(three, four)
END PROGRAM Run
Results:
( 999 , 888 )
Question 2
----------
'Leechford' 5
13.17 42.62 46.24 5.51 23.23
The first line is a quoted string and an integer, which gives the count
of the number of reals on the next line.
The subroutine takes a value of type Midden and prints its contents
(you need not quote the string). If the name and array are both
null, it prints a message saying so.
Specimen Answer
---------------
2.1 Code:
MODULE Midden_mod
IMPLICIT NONE
TYPE Midden
CHARACTER(LEN=16) :: location
REAL, DIMENSION(:), ALLOCATABLE :: data
END TYPE Midden
CONTAINS
create%location = name
ALLOCATE(create%data(1:UBOUND(values, 1)))
create%data = values
END FUNCTION create
2.2 Code:
PROGRAM Run
USE Midden_mod
IMPLICIT NONE
TYPE(Midden), DIMENSION(10) :: data
CHARACTER(LEN=25) :: name
REAL, DIMENSION(100) :: temp
INTEGER :: K, N, err
LOGICAL :: OK = .True.
DO N = 1, 10
CALL print(data(N))
ENDDO
END PROGRAM Run
Note that the exception handling in the I/O is not best practice.
Results:
Question 3
----------
Add PRIVATE to hide the components of the derived type used in question
2 and check that the program and module still work. Do so to the one
used in question 1 and see what fails.
Answer:
The compiler objects to both the use of a constructor and accessing the
components (i.e. using `%'), but not to the simple assignment. Derived
types with private components are most simply treated as purely opaque
types.