This `virtual laboratory' contains experiments in modelling and simulation using a number of different techniques.
Before starting any of the experiments, read about the philosophy of modelling programming languages (Section L2.1)
The left hand side of the equation should be typed in the box provided using only `x' as the unknown.
The expression must use JavaScript syntax which accepts the usual operators except either `^' or `**', as well as exp, sqrt, sin, cos etc.
log is natural log. To raise to a power use pow(x,n) .
The `Step' button takes a single bisection step. Click it again to to another step. The `Solve' button takes 20 steps. Another click gives another 20.
One of the most widely used general languages historically is Fortran (section L3.1), and many large models, particularly of special purpose process units such as reactors will be found in this language. We have given a number of examples of algorithms in Fortran.
A very large number of models in the last few years have been written to run in spreadsheet systems. The spreadsheet is extremely convenient, as it can be found on every PC and most palmtops. However, spreadsheets have several very serious disadvantages:
As a result of the above, manually generated spreadsheets cannot be regarded as a safe modelling tool! The most important property which a model must have is that it should be UNDERSTANDABLE. This is much more important than that it should be correct!
However, the spreadsheet is so universal and so convenient for running models (as opposed to building them) that we have developed a simple modelling language. which avoids most of the above problems by providing a high level description of the model, but generates a spreadsheet which can be used on any PC.
The main purpose of this language is thus to facilitate the production of documented models. In its simplest version (release 2.1, September 1998 for algebraic equations) it provides no solution facilities not available to the standard spreadsheet system.
The model itself should be easy to read and self explanatory. In addition, a modelling system may usefully provide additional and/or summarised information information about the model.
The model can, and should, contain plentiful comments which explain what different parts of it mean. These are preceded by any of the symbols `!', `%' or `#' and are ignored by the modelling system.
The language can be used to model both steady state systems, represented by algebraic equations, and dynamic systems represented by differential-algebraic equations. The discussion below pertains specifically to algebraic equations.
variableand
end variable
The ability to give user specified names to variables is a key feature of any modelling language, and the choice of `meaningful' names is an important factor in making a model comprehensible.
For example, a very simple model is to be used to calculate the mass density in kg/m of an ideal gas at a given Kelvin temperature and pressure in atmospheres. We are also given the molecular weight of the gas. We decide that we need variables to represent the specific molar volume and mass and molar densities. Although the pressure is given, and is not an unknown or variable (it is a parameter, see below) the Gas Law requires pressure in pascals which the model will require to calculate, so we require a variable for this pressure, giving 4 variables in all. The model instructions are then:
variable vmolar ! specific molar volume romolar, romass ! densities P ! N/m2 required for gas law end variableNote the use of both standard symbols for standard quantities, e.g. P for pressure, and mnemonic names for other quantities.
parameterand
end parameter
However parameters must be given values, which is done as in the example below.
parameter MW = 16 ! methane, kg/kmol T = 273 ! K temperature Patm = 1.0 ! atmospheres, sensible units R = 8.314 ! gas constant in SI units: gmol, Pa etc endNote the appearance of R, the gas constant in this section. strictly, R is a true constant, having a fixed value, but most modelling languages do not distinguish these from parameters.
The modelling languages requires initial values for parameters, but these can be changed once the model has been generated. Do not however change the values of constants like R!
equationand
end equation
The simplest version of the model generator requires that all equations be written as formulas, and that every unknown (variable) appears once and only once on the LHS of a formula. This approach is discussed in the general introduction to algebraic equations (section 3.1). The equations do not have to be ordered as described in section 3.2, as this is done by the spreadsheet system. The equations section for this model is as follows:
equation vmolar = R*T/P ! m3/gmol romolar = 1/ vmolar ! gmol/m3 romass = romolar*MW/1000 ! kg.m3 P = Patm * 101300 end equation
The order of the sections is currently fixed and is slightly different from that in we have introduced them. The more logical, and required, order is:
model SomeNameand
end model`SomeName' is a name which you can give the model.
Note that all names used in the model must be a combination of letters and number only , and must start with a letter. Upper and lower case letters are treated as being the same.
model idealgas ! ideal gas density calculation parameter MW = 16 ! methane, kg/kmol T = 273 ! K temperature Patm = 1.0 ! atmospheres, sensible units R = 8.314 ! gas constant in SI units: gmol, Pa etc end variable vmolar ! specific molar volume romolar, romass ! densities P ! N/m2 required for gas law end equation vmolar = R*T/P ! m3/gmol romolar = 1/ vmolar ! gmol/m3 romass = romolar*MW/1000 ! kg.m3 P = Patm * 101300 end equation steadystate end model
You can copy it from here, and generate a spreadsheet to run it here.
An Excel-compatible spreadsheet translation may be copied from here and when loaded should look like this.
The model generator does not itself provide a mechanism for solving equations iteratively. However the facility for solving single equations using a `goal seek' facility is available in nearly all spreadsheet systems.
We therefore use the model generate to provide evaluation of the function or left hand side of the equation which it is required to reduce to zero. An illustration for one of the exercise examples is shown below.
model single variable x, fx end variable equation fx = x + @ln(x) end equation steadystate end modelNotice that there is only one equation here, but there are two quantities declared as variables, x and fx. The latter is not really an unknown, it is the function value which is to be reduced to zero. using `goal seek'. (Note in passing the form of a function call in the model generator; the function name must be preceded by a`@' whether or not the spreadsheet requires that convention. Also that Excel appears to interpret LOG as base 10, although the JavaScript solver referred to elsewhere, most other programming languages and mathematical convention treats this as the natural log!)
Alternatively, if the spreadsheet does not have a search facility, it is possible, for single equations, to use the logic of a bisection search by hand.
The above model program can be copied from here. Copy it and try both an automatic solution (if available) and a hand directed trial and error.
The model generator is here.
A .slk format translation is already available here. You can copy it and load it into Excel. Cell C5 contains the unknown x (defaulted to zero) and cell D5 contains the function value fx. Initially D5 displays an error message because an attempt has been made to calculate the log of zero.
Change C5 and observe changes in D5. Note that values of 0.1 and 1.0 bracket the solution. Try choosing values by hand to `home in' on the solution when fx in D5 will be nearly zero.
To use the `Goal seek' feature of Excel, proceed as follows:
This may seem a rather cumbersome procedure for dealing with single equations, but it becomes useful where we have a set of equations which can be solved by iteration in a single variable, see section 3.5.1
A model described in the language may be turned into Lotus-123 compatible spreadsheet code, saved and run using essentially any spreadsheet system, including Excel and the Psion spreadsheet.
The language allows the creation of differential-algebraic equation (DAE) models, although the solution facilities for algebraic equations in spreadsheets are somewhat limited.
The use of the language will be described using a simple example.
dx/dt = (z-x) / T1
dy/dt = (x-y) / T2
z = - A y
The initial conditions are: x = y = 0 at t=0
In the language, the equations are described as follows.
model test ! Simple example dynamic model parameter ! These are quantities which are known, but which ! the user may wish to change to see their effect A=1 ; t1=1 t2 = 2 ! They will normally be given initial values end parameter variable ! These are the variables or unknowns of the problem x1 = 0 ! They may be given initial values x, y ! or not if preferred end variable Equation ! These are the equations y = x*x ! This is an algebraic equation ! The are differential equations... .x1 = (-A*y-x1)/T1 .x = (x1-x)/T2 end equation initial ! This is another way of giving a variable an initial value x=0 end initial schedule ! This section defines how the model will be run deltat=0.5 ! Timestep steps=50 ! number of timesteps end end modelIt can be seen that the model consists of five sections, each introduced by a keyword, and terminated by the word end.
The model instructions are written in normal `computer notation'. There can either be one per line, or multiple instructions on a line, separated by either `;' or `,'. Multiline instructions are not allowed.
Any line beginning with a `!' is a comment and is ignored.
Each of the sections will be described briefly.
parameter ! These are quantities which are known, but which ! the user may wish to change to see their effect A=1 ; t1=1 t2 = 2 ! They will normally be given initial values end parameter`Parameters' as distinct from `unknowns' in a mathematical problem are quantities which the user knows, but which might be changed at some point to see their effect. It is convenient to give them symbols rather than simply numbers.
A parameter may often be a physical property, e.g. a heat capacity, which can appear several times in a set of equations. If the parameter is defined in one place, then making one change will result in the new parameter value being used throughout the model.
This section of the model enables parameters to be defined and given values if required. If no values are given the parameter defaults to zero. New values can be given when the spreadsheet has been generated.
variable ! These are the variables or unknowns of the problem x1 = 0 ! They may be given initial values x, y ! or not if preferred end variableThe variables are the unknowns in the model. As discussed elsewhere, there are two sorts of variables in DAEs, those which may appear as derivatives in the differential equations, and the `algebraic' variables which do not. This distinction is not made in this section, which serves mainly to define the names to be used for variables.
Variables may be given initial values in this section, as illustrated. These are relevant only for ODE variables, and will be ignored for algebraic variables. Initial conditions may also be specified in a separate section of the model.
Equation ! These are the equations y = x*x ! This is an algebraic equation ! These are differential equations... .x1 = (-A*y-x1)/T1 .x = (x1-x)/T2 end equationThis is the main part of the model and contains the differential and algebraic equations which describe the model. Note the following:
initial ! This is another way of giving a variable an initial value x=0 end initialThis section provides another place where initial values may be given for variables. If no initial values is set for a variable which requires it, then it defaults to zero.
schedule ! This section defines how the model will be run deltat=0.5 ! Timestep steps=50 ! number of timesteps endThis section describes how the model is to be run. At present this may specify any two of the three quantities:
There may be more than one of each of the sections, e.g. a variable section, an equation section, another variable section and another equation section. However, all variables must appear in a variable section before they are used in an equation.
Only one schedule section is allowed.
For the stand alone solver you may specify a full matrix in which you may later enter values for any or all of the coefficients. Alternatively you may specify a semi-sparse matrix in which you identify coefficients which are nonzero. The values of nonzeros may be entered into the spreadsheet cells later, but zero coefficients cannot be changed and will always be zero.
For the embedded solver, coefficients and constants may be either numerical values or spreadsheet cell names (given as RnCm). Cell names must be given for all of the unknowns.
In summary the 3 options are:
If you are outside the University of Edinburgh, the spreadsheet you create will be emailed to you, so you must give a valid email address. If you would like to try a `full matrix' spreadsheet there is a 12 equation solver available for direct downloading in three spreadsheet formats from the links below. You can save these in your local filespace and load them into any compatible spreadsheet.
If you are running from behind a firewall, you may find it more reliable to have the model emailed back to you.
Most of these programs were written by chemical engineers. The ability to write large and complicated programs is much less likely to be required of chemical engineering graduates nowadays, but you may well encounter programs which you may have to understand or even modify in industry.
Fortran, as indicated, comes in various dialects, which have evolved from the earliest in the late 1950s through Fortran 66 (1966), Fortran 77 (1977, still sometimes encountered) to the current Fortran 90 which we will use. The earlier versions are rather horrible and unsuitable for teaching purposes. But if you do have to deal with, say a Fortran 77 program, then your knowledge of Fortran 90 and a reference manual should be enough to enable you to work out what is going on.
Fortran has all the necessary features required to represent a mathematical model. These are described in the following sections.
A key feature of the language is that the formulas are evaluated in the sequence in which they are written by the programmer. This makes Fortran different from a spreadsheet system which (in the more sophisticated versions) works out a `correct' order for evaluating its formulas.
In fact a Fortran program consists of other `instructions' all of which are carried out in the order in which the programmer has specified them. Creating this `ordered list', `recipe' of instructions or algorithm is what most of programming is concerned with.
We will describe the instructions necessary to create a simple model in the next section. It is helpful to think of Fortran instructions as falling into three main categories:
P v = R T
Remember that here v is the specific molar volume, i.e. m³/kmol, and that if R is 8.314 J/mol/K then pressure P must be in pascals.
Simple programs which consist essentially only of formulas to be evaluated can be developed in the following stages:
The other equations are concerned with calculating molar
density
and mass density
from specific volume:
= 1 / v
=
M / 1000
Here M is the molecular weight in kg/kmol. The factor 1000 arises of course because R is in gram mols. If pressure Pa is given in atmosphere, then this must be converted to pascals using the relationship:
101300 Pa = P
v = R T / P
= 1 / v
=
M / 1000
P = 101300 Pa
P = 101300 Pa
v = R T / P
= 1 / v
=
M / 1000
The working part of the program thus now becomes:
P = 1.013e5 * Patm ! P in Pa v = R * T / P ! specific volume in m3/mol rhomols = 1 / v ! in mol/m3 rhomass = rhomols * M / 1000 ! ... in kg/m3Note particularly the `comments' preceded by `!' which are ignored by the program, but help the reader to understand what is going on - remember, the most important thing about a program is that it must be intelligible to a reader!
Add the `housekeeping' ...
Before reading this section you may wish to refer to notes
on
how to write simple Fortran programs
(section L3.2).
The housekeeping can be divided into the parts that make sense to the fairly uninitiated, and the other bits that just have to be learned by rote!
The first of these include the definition of `declaration' of variables and parameters. these must appear at the beginning of the program, logically enough, before any of the variables or parameters are used.
To declare variables which will contain general or `real' numbers we say:
real :: P, v, rhomols, rhomassTo declare parameters or constants we use a similar construction, but include the values to be specified, e.g.:
real, parameter :: Patm=2.0, T=298, R=8.314, M=16Having calculated the solution in terms of variable values, these have to be communicated to the outside world by means of the computer instructions `print' or `write'. The simplest form for these here is:
print *, 'Specific molar volume is ', v, ' m3/mol' print *, 'Molar density is ', rhomols, ' mol/m3' print *, 'Mass density is ', rhomass, ' kg/m3'The text printed out with the values should be regarded as an essential part of the program. Logically, the values cannot be printed out on the screen until they have been calculated, so these instructions come after the calculation part.
The program is required to start with the word program and be given a name, and to end with the words end program , followed by the program name. Good programming practice dictates a comment on the purpose of the program, and author details. Finally the statement implicit none should be present to override the default implicit naming scheme.
The complete program is thus:
program pvt !this program calculates the specific molar volume, molar density !and mass density for the given data using the ideal gas law !JWP 11/12/98 implicit none real :: P, v, rhomols, rhomass real, parameter :: Patm=2.0, T=298, R=8.314, M=16 P = 1.013e5 * Patm ! P in Pa v = R * T / P ! specific volume in m3/mol rhomols = 1 / v ! in mol/m3 rhomass = rhomols * M / 1000 ! ... in kg/m3 print *, 'Specific molar volume is ', v, ' m3/mol' print *, 'Molar density is ', rhomols, ' mol/m3' print *, 'Mass density is ', rhomass, ' kg/m3' end program pvtYou can copy the program from here to try it for yourself.
Examples will be given on the assumption that the Edinburgh Portable Compilers EPCF90 compiler is being used on a Unix system.
The aim is to get you writing simple programs quickly, not to teach you all about writing complex programs. If you want to find out more about the Fortran 90 (F90) language and `real' programming, we will provide hypertext links and a bibliography which you can follow up.
program hello print *, 'Hello World!' end program hello
A program which consisted just of the first and last lines would be a `syntactically acceptable' program, i.e. it could be compiled and run as described below. However, it wouldn't do anything, and so would be pointless.
The `useful' bit of the program is the middle line:
print *, 'Hello World!'What this does is to print on the computer screen the text:
`Hello World!'This instruction is formally called a `procedure call'. It causes something to be done; what is does is predefined by the word `print' which the language recognises as meaning `print or otherwise display some information'.
What is printed is determined by the programmer-supplied text included between the two quote symbols. (NB these are both the same single closing quote on the keyboard.) Any text (except for another single quote!) which appears there will be `printed out'. A piece of text in quotes is referred to as a `text string' or `character string' or sometimes just a `string'.
And the `*,' ? For the time being, please just accept that it is necessary. If you leave it out, the program will not work!
For future reference: you will encounter other types of procedure call in the language. All have the properties that (a) what is done is predefined and associated with the name of the procedure, here `print'. How it is done, or with or to what is usually specified by the programmer, as is the case here with the text to be printed.
Once you have saved the program it must be translated from a form which you can read (sometimes called the `source language') into a form which the computer can run. This process is called `compilation'.
Assuming you have saved the hello.f90 in your current working directory, to compile the program type:
epcf90 hello.f90`epcf90' is the name of the command which runs the compiler. It is always followed by a space and then the name of the source language file. (And of course `return' at the end!) The computer will print a number of messages which should look something like this:
... epcf90 hello.f90 EPC Fortran-90 Version 1.5.1.7 Sparc:031097:145225 Copyright (c) 1993-1997 EPCL. All Rights Reserved. hello.f90 program HELLO 3 Lines CompiledIf you look at your files, you will find that there are a number of new ones, mostly with peculiar names. One in particular will have the name a.out and this is the `compiled' or computer runnable version of your program. To run (`execute') this just type:
a.outand see what happens.
textedit hello.f90
EPCF90 and most versions of Fortran also allow instructions to be separated by a semicolon `;'. Thus the following are equivalent:
print *, 'This is my 1st program..' print *, 'Hello!'And:
print *, 'This is my 1st program..' ; print *, 'Hello!'Try these two forms.
print *, 'This is my 1st program..' , 'Hello!'Try this. This has a somewhat different effect from having the same message in one text string.
A properly commented version of hello.f90 is here.
program hello ! Very first example program ! Written by JWP, September 1998 ! print *, 'Hello!' ! A single procedure call ! This will print a message on the screen ! end program helloIf you compile this you will find it behaves exactly as the shorter version did.
program arithmetic ! program to do a calculation ! Written by JWP 29/10/98 print *, 'This program calculates 23.6*log(4.2)/(3.0+2.1)' print *, 23.6*log(4.2)/(3.0+2.1) ! Note the effect of the presence or absence of quotes! end program arithmeticYou can copy it from here.
Save, compile and run this program.
Important Point! Notice the difference between how 23.6*log(4.2)/(3.0+2.1) is treated:
In the second it is treated as an expression and is evaluated using the computer's rules for arithmetic. These are more-or-less the same as the conventional rules but include the following:
The point about being able to print two quantities in the same print instruction may now become more obvious. We can have both expressions and strings in the same instruction so as to print on the same line, e.g.:
print *, 'This value is ', 23.6*log(4.2)/(3.0+2.1)
Consider the following program which you should copy from here:
program variable !this program evaluates an expression !written by JWP 28/10/98 real :: x ! This defines a variable called x ! x = 23.6*log(4.2)/(3.0+2.1) ! This gives it a value ! print *, 'The value of x is ', x ! .. and then print it out end program variableThe overall effect of this is essentially the same as the previous program, except that it involves a new kind of entity, vital to effective programming, called a variable. It is so called because the value which it takes can be changed by equating it to different expressions on the right hand side of an instruction which always takes the form:
variable_name = expressionThe form of this assignment instruction is identical to that of evaluating a formula, as discussed in section 3.1. It is convenient to think of a variable in this context as being the same as a variable in a set of equations, although it is in fact a more general and flexible concept than that. At the very least, the use of variables facilitates the writing and developing of programs, and makes them easier to understand.
Consider the use of a program to evaluate expressions involving variables whose values depend on other variables, i.e. a set of equations. This involves a slightly modified version of the previous program:
program variable !this program evaluates two expressions and prints the result !written by JWP 29/10/98 real :: x, y ! This defines variables called x and y ! y = sqrt(23.6) ! calculate y x = 23.6*log(1.0+y)/(3.0+y) ! calculate x which involves y ! print *, 'The value of x is ', x print *, 'The value of y is ', y ! .. and then print them out end program variableYou can copy this program here.
The instruction:
real :: x, yis called a declaration of the variables x and y. It consists of the word real to indicate that the variables will be used to represent general or real numbers (as opposed to whole numbers or integers, which can also be be used in certain circumstances, see later).
It is followed by a list of the names to be given to the variables. These can be any combination of letters or numbers, but must start with a letter. The names may not include spaces, but the underscore character `_' may be used and is very useful in improving readability.
| Invalid variable names | Valid variable names |
|---|---|
| 1st | first |
| not correct | this_is_correct |
| _invalid | x4 |
| a-bad-name | temperature |
| no.dots.allowed | volume |
The two colons `::' are part of the illogical richness of Fortran. They may sometimes be left out, but this is not advised.
You should try to choose variable names which help the readers understanding of the program. In general you should not use meaningless name like `x' and `y', unless these have been used in the specification of the problem. Where standard symbols exist, e.g. `P' for pressure or `T' for temperature, use these with variants, e.g. T1, T2 or use descriptive names, e.g. T_reactor, Column_Pressure. Names can be up to 31 characters long, but typing very long names becomes tedious (and they can be mis-spelled). NB. Fortran is case insensitive. Thus FIRST, First, and first all refer to the same variable.
implicit noneat the top of every program. This is a sign of good programming style or practice.
Parameters are declared in a similar manner to variables, but must be given their constant values at the same time as they are declared. For example, pi should remain constant throughout a program:
real, parameter :: pi=3.14
In this very simple program we have not really exploited the concept of variables whose values may be changed. It would have been possible to decide that the entity we have called y was a parameter. This has been done in this second version of the program. since a name like `y' is typically associated with the unknown in a problem, the name has been changed here, although this is not strictly necessary.
program variable !this program demonstrates the use of parameters !written by JWP 29/10/98 implicit none real :: x ! This defines variable called x real, parameter :: fixed_y = 4.86 ! x = 23.6*log(1.0+fixed_y)/(3.0+fixed_y) ! calculate x which involves y ! print *, 'The value of x is ', x print *, 'The fixed value of y is ', fixed_y ! .. and then print them both out end program variable
To give you practice in `debugging' programs, here is one which will fail to compile.
program crunch ! there is/are something(s) wrong with this program ! find out what and correct it !written by JWP, 23/10/98 implicit none real :: a, b, c a = 10.0 b = 23.5 c = a*b print *,'What is wrong with this? print *, 'Nothing now..' print *, c is, c ! end program crunch
And here is another which may appear to be correct but will fail.
program crunch_again ! This program has a different kind of error from the first one !written by JWP 23/10/98 implicit none real :: a,b,c print *,'Does this work?' a = 10.0 c = 3.5+a c = c*b b = 23.0 print *, 'a, b and c are:', a, b, c ! end program crunch_again
Copy these, try them and correct them.
It is very easy to make a program repeat a sequence of instructions an indefinite number of times. However the example we are about to give is an example of very bad programming practice which should never be emulated! it is provided here simple to illustrate how easy concept is to implement.
Any instruction or sequence of instructions will be repeated indefinitely if it is preceded and terminated respectively by the two instructions shown below:
do ! instructions to be repeated here end doIt is considered good practice to indent every instruction between the do and end do by three character spaces.
Here is a very simple program which illustrates this. It is a very bad program, because as written it will never stop running!
program zap ! This is not a good program !!! ! Before running it, make sure that you can identify ! `panic button' which stops programs on you computer. ! On Unix systems it is simultaneously `Control' and `c' !JWP 29/10/98 implicit none do print *, 'ZAP!!' end do end program zap
If you are going to compile and run it, first make sure you know how to do an `emergency stop' on your computer system. This is usually achieved by pressing simultaneously the letter `c' key and the key marked `Control' or `Cntrl'.
The principle should however be clear from this example....
To be useful, the repetition clearly needs to be under rather better control! A useful way of achieving this is to count the number of repetitions and stop after a predetermined number. the means of doing this is described next.
do times = 1, 10 print *, 'ZAP!!' end doThe only difference between this and the previous example (so far) is in the first line. The interpretation of this can be consider to be as follows:
Like any other variable, times must be declared at the beginning of the program, before it is used. However, because times will have values which are always whole numbers or integers, the form of the declaration is different from those seen so far:
integer :: timesHere is the complete, and much improved, program:
program zap ! This is a better program !!! ! JWP 29/10/98 implicit none integer :: times do times = 1, 10 print *, 'ZAP!!' end do end program zapCopy it from this link.
The counter variable has other uses than just stopping this program loop. Its value is 1 the first time round, 2 the second and so on.
Try changing the instruction in the loop to:
print *, 'ZAP times ..', timesand confirm this.
There are a number of other uses for integer variables, but a major one is controlling loops like this. The number of times the loop is to be repeated, and, associated with it, the starting and finishing values of the counter variable, must be specified before the do instruction is encountered, but may in fact be general integer expressions.
Note that in this example the numbers `1' and `10' were written without decimal points, whereas these were required for numbers in the general expressions encountered previously. This is because these starting and finishing values must be whole numbers. The following would also be allowed, try them, printing out the counter value on each loop:
The following sections provide programs to illustrate the use of repetition loops and a number of other programming techniques. You are encouraged to copy and modify these as described.
program squares ! calculate squares ! JWP 29/10/98 implicit none integer :: number, square integer, parameter :: max = 10 ! print *, 'Squares of numbers from 1 to', max ! do number=1, max square = number ** 2 print *, 'Square of ', number, ' is ', square end do ! end program squares
Modify it to calculate and print the squares of numbers from 1 to 15 by altering only a single character in the program.
program squares ! calculate squares ! JWP 29/10/98 implicit none integer :: number integer, parameter :: max = 10 real :: real_number, root ! print *, 'Table of square roots' print *, 'Number ', ' Square root' ! do number=1, max real_number = real(number) root = sqrt(real_number) print *, number, root end do ! end program squares
Square roots can also be calculated by raising numbers to the power 0.5, or in Fortran writing ** 0.5 Check that this gives the same answers, and modify the program to tabulate cube roots.
integer :: i real :: x ..... do i = 1, 12 x = i * 0.25 print *, x, sqrt(x) end do ...There is a more general way of doing this described later.
x = x + 3If you are in any doubt about what will happen, complete the following program segment by adding the necessary additional instructions, and run it.
..... x = 4 print *, 'x was', x x = x + 3 print *, 'x now is', x .....There are many circumstance where it is convenient or necessary to use this kind of `recursive' instruction. One such is illustrated in the example below. Complete this program and use it to compute some factorials. Check to see that it gives the correct answers! 5! is 120, 10! is 3628800. What happens when you try to calculate 100! which is about 10158?
n! = 1 . 2 . 3 .... (n-1) . n
Consider the following program segment:
... integer :: fact, n, i n = 5 fact = 1 do i=2,n fact = fact * n end do ...
Recall that this quantity is equal to ½(n+1)n.
real :: Tprop, Tbut, T, dT integer :: i, n .... n=10 ! number of points ... dT = (Tbut - Tprop) / (n-1) ! divide range by number of INTERVALS ! .. this gives size of interval T = Tprop ! Start here do i=1, n print *, T ! the current temperature T = T + dT ! next temperature ... end do ...Exercise: Write a program to tabulate the vapour pressures of propane and butane at 12 equispaced temperature points between their respective 1atm boiling points.
Consider the following sequence of instructions:
.... if ( a >=0.0 ) then print *, 'a is zero or positive' else print *, 'a is negative' end if ...Their implication should be fairly obvious. If the variable a has a value greater than or equal to zero, then the instruction:
print *, 'a is zero or positive'is carried out and the appropriate message printed. On the other hand if this is not the case (`else') then the instruction:
print *, 'a is negative'is obeyed.
The program would then proceed with any instructions following end if.
The above is an example of the most general form of the conditional or `if' construction used for decision making in computer programs. The lines between then and else and between else and end if may contain any sequence of legal `executable' instructions. (This excludes `formal' instructions like real :: or end program.) As with do loops, note the indentation of the instructions by three character spaces. This is to give clarity to the program.
As an example of using this for of the `if' construction, here is a program to solve a quadratic equation using the quadratic formula. The program tests to see if the equation has real roots before proceeding with the solution.
program quadratic ! solve a quadratic with real roots ! JWP 22/10/98 implicit none real :: a, b, c, d, x, x1, x2 ! a = 1 b = 2 c = -3 ! print *, 'Solving quadratic:' print *, a, ' x**2 +', b, ' x + ', c ! check nature of roots.. ! d = b**2 - 4.0*a*c ! if (d<0) then print *, 'Has complex roots.' else x1 = (-b + sqrt(d)) / 2.0 / a x2 = (-b - sqrt(d)) / 2.0 / a print *, 'Roots are:', x1, x2 end if ! end program quadratic
Three other forms of the `if' construction are useful. Sometimes it is only required to carry out a series of instructions if a condition is satisfied. This is achieved by the following. Note that the symbols /= are the Fortran version of the `not equals' sign.
if (a /=0.0 ) then print *, 'a is not zero so can be a divisor' x = 1 / a end ifNotice that in this case nothing happens if the condition is not satisfied, i.e. if a is equal to zero. In this case the value of x is not set by any instruction shown in this program segment.
If only a single instruction is to depend on the outcome of the condition then it may be shortened to:
if (a /= 0.0 ) x = 1 / aFinally, by using else if several conditional statements may be chained together:
if(a > 0) then print *, 'a is bigger than 0' else if (a < 0) then print *, 'a is less than 0' else print *, 'a must equal zero' endif
`<' and `>' have their obvious forms, <= stands for `less than or equal to' and hence the obvious interpretation of >=.
'Not equal to as above is /=.
Confusingly `equal to' must be written with two equals signs, i.e. as ==.
program random ! generate some random numbers ! JWP 22/10/98 implicit none real :: x real, external :: rand integer :: i do i= 1, 10 x = rand(0) print *, x end do end program random(Note the special form of declaration: real, external :: rand which says that rand, a function to generate random real numbers in the range 0.0 to 1.0, is to be found outside, i.e. `external' to the program. This is one of a number of useful function which exist in special libraries.)
Copy the program from here and modify it (a) to print only random number greater than 0.5, and (b) to count and subsequently print out the number of such numbers.
exitwhich can only appear inside a do .. end do loop causes a immediate termination of the loop. The program carries on at the instruction following end do.
The do may be either with or without a counter variable. In the latter case the count is overridden and the loop stops at whatever is its current value.
The exit instruction is normally always the subject of a condition, e.g.:
if (TK <= 0.0) exit
There is another method of stopping a do...end do loop using a conditional statement:
do while (x == 1) . . . end doThis is equivalent to saying:
do if(x /= 1) exit . . . end doThe do while format is preferred by purists as the loop can only terminate after a complete iteration.
This section has been left until last because we do not particularly want to encourage you to write `interactive' programs until you are reasonably experienced in writing self contained programs.
All the programs shown to you so far have been `self contained' in that all the information required for the program to work is contained within the program.
All your handin examples should normally be written in this way. A major advantage of such a program is that it's operation can be checked by reference only to a printout of the program. A disadvantage is that every time we want to change a parameter then the the program file must be edited and recompiled.
We will now discuss how to make programs more `user friendly' and `interactive'.
When ever the computer encounters an instruction like the following:
read *, xit does two things.
First it stops and waits for the user to type something on the keyboard. Secondly it interprets whatever has been typed in the context of what kind of variable x has been declared to be, and if it can, assigns whatever has been typed in to x. This should be clear from the following complete program which you might like to copy and try:
program readin real :: x print *, 'Type in a valid real number:' read *, x print *, 'The number you typed was ', x end programYou can copy the program from here. If you are trying it out, see what happens if you type either an integer or something that isn't a valid number.
Note that the variable which appears in the read instruction must be a variable which is not declared as a parameter.
It is possible in principle to have more than one variable in the list following read *, but this is not a good idea when numbers are being read from the keyboard. (They can come from elsewhere, as will be discussed later.) Also you should always print out a message to remind the user what to type in.
We have already encountered an example of a `procedure call' in the print instruction. we can (loosely) define a procedure call as an instruction which `causes something to happen' other than, or in addition to, simply assigning a value to a variable.
Procedure calls have two main characteristics. Firstly the procedure invoked by the call has a name, e.g. print. Secondly there are usually one or more arguments which specify with what the procedure is to perform whatever it is meant to do.
The name of the procedure is fixed, but the programmer chooses the arguments. Thus:
print *, 'Hello'has as an argument the character string 'Hello' and so that is what gets printed.
print *, whateverhas the variable whatever as an argument, so its value gets displayed.
print is a rather special procedure which is built into the language. There are some others like it which you will encounter later. Another type of procedure is the subroutine procedure or simply a subroutine. These are not built into the language but are available in special libraries or can be defined by the programmer.
At this point we will introduce one example of a subroutine procedure which is available in a standard library, and another user defined subroutine which will be supplied to you. Writing your own subroutines will be covered later.
A subroutine procedure call which activates the procedure consists of the word call followed by the name of the procedure and then a list of its arguments (if any) in brackets.
system takes a single argument, the name of the Unix command as a character string in quotes, e.g. 'ls'
The following complete program will cause a listing of your files to be printed on the screen when the compiled program is run:
program list_files
! An minimum example of a subroutine procedure call
! JWP 22/10/98
implicit none
! This instruction makes it work:
call system ('ls')
!
end program files
A version of this program
can be copied from
here. You can try changing
`ls' to `ls -l' and see what happens.
The above versions of the program are the minimum size and complexity required to access this subroutine. If you learn more about the Fortran 90 language you will discover that a stricter and more pedantic form is available. For completeness we have supplied a `long' version of the program here.
subroutine atomic (element, number, value)
! look up atomic no. and weight of some common elements
! JWP 22/10/98
implicit none
character *(*), intent (in) :: element
real, intent (out) :: value
integer, intent(out) :: number
integer, parameter :: na=12
integer, dimension(1:na), parameter :: an = &
(/17, 35, 11, 20, 6, 1, 7, 8, 16, 9, 53, 19/)
real, dimension(1:na), parameter :: aw = &
(/35.5, 79.9, 22.0 , 40.1, 12.0 , 1.0, 14.0, 16.0, 32.1, 19.0, 126.9, 39.1/)
character*48, parameter :: ename = &
'Cl,Br,Na,Ca,C ,H ,N , O ,S ,F ,I ,K '
integer :: i
character *(2) :: copy
!
copy = adjustl(element) // ' ' ; copy = copy(1:2) ! fiddle with length etc.
i = index (ename,copy) ! look up element in list
if (i==0) then
print *, 'I do not have data for element ' // element
value = 0.0 ; number = 0
return
end if
!
i = (i-1)/3 + 1 ! Table entry number
value = aw (i)
number = an (i)
end subroutine atomic
To make use of this subroutine you must do two things. Firstly, as in the previous example, you must write a program to `call' the subroutine. A typical call might look like this:
call atomic ('K', Kno, Kwt)
Here the string 'K' determines that it is the
properties of potassium which are required. Kno
is a variable which you the programmer must have declared
as an integer in your program. The subroutine
call will set this to the atomic number of potassium.
Kwt must have been declared as a real
variable and will contain the corresponding atomic weight.
Note that these can be any appropriately declared variables and can
thus have any names that you want to give them.
To use use this subroutine you also need to let your program know about it. The simplest way to to this is to copy the whole subroutine from the link and put it in the same file as the program you are writing which will use the subroutine. the layout of the file will then be as shown below.
program atom_stuff
! My own program, declarations etc. go here..
real :: Kwt, ...
....
call atomic ('K', Kno, Kwt)
print *, 'Properties of Potassium..', ...
...
end program atom_stuff
! What follows is just copied..
subroutine atomic (element, number, value)
! look up atomic no. and weight of some common elements
... etc
...
end subroutine atomic
It is not necessary that you understand how atomic
actually works, but it is useful to note the existence of
variables which will hold character strings. a declaration
such as:
character *(2) :: elementdeclares a variable called element for storing not numbers but letters, up to a maximum length of two characters.
The intent attribute informs a programmer using a subroutine whether a specific argument is to be:
The structure of a subroutine is quite similar to that of a program:
function calc(x) !this function evaluates a simple formula !Ross Andrews 26/10/99 implicit none real, intent(in) :: x real :: calc calc = x**2 + 3*x - 4 end function calcThe main point to note here is that the function name, calc is declared as a variable, and assigned to, in the function. This will be the value returned to the calling program. In the calling program, the function calc must be declared as follows:
real, external :: calci.e. external to the main program, and can be used as:
y = calc(43.2)
Fortran has a number of built-in functions, some of which you have used already:
The variables in the subroutines and functions (subprograms) described here are independent from the main program variables, and vice versa. What this means in practice is that subprograms cannot refer to main program variables, unless they are passed as an argument, and likewise in the main program.
There may be some confusion as to whether a subroutine or a function best suits a particular task. Whilst this is down to the preferences of the individual programmer, there are a number of guidelines: