Difference between revisions of "Fortran2"

From SourceWiki
Jump to navigation Jump to search
Line 76: Line 76:
 
Now, user-derived types offer us something simple, yet potentially extremely powerful--we can group things which are related into a single composite type.  That is, we can group all the things that belong together into a single entity and then pass that entity around as work is done by our program.  This is a huge bonus when designing and maintaining a reasonably sized program.  As we'll find out with growing experience, user-derived types are, pretty much, the best thing since sliced bread!
 
Now, user-derived types offer us something simple, yet potentially extremely powerful--we can group things which are related into a single composite type.  That is, we can group all the things that belong together into a single entity and then pass that entity around as work is done by our program.  This is a huge bonus when designing and maintaining a reasonably sized program.  As we'll find out with growing experience, user-derived types are, pretty much, the best thing since sliced bread!
  
(User-derived types represent a first step down the road towards ''object-oriented programming''--the current best way to organise your program. LINK)
+
(User-derived types represent a first step down the road towards [http://en.wikipedia.org/wiki/Object-oriented_programming ''object-oriented programming''].  A step in the right direction in terms of designing programs.)
  
 +
OK, enough of the spiel.  Let's take a look at '''user-types.f90'''.
  
DAB radio.  Channel has frequency and name.  Array of channels.
+
I recently bought a DAB radio and I'm as pleased as punch with it.  One of the great features is that you an select a radio station by name, push a button and bingo!, you're tuned in.  No more scrolling around the dial trying to get clear receptionThat makes it really easy to 'station hop' and find something good on.  Now, imagaine we were designed a program to implement a DAB radio.  It's natural that we would want to keep the radio station name and tuning frequency (still needed, of course, but only by the machine as it now does the tuning for you) together in a single bundleThey ''belong'' together, after all, and are of different intrinsic types--a chacter string and a real number. In the program it looks like:
  
Have written example, will write up notes - GW.
+
<pre>
 +
  type station
 +
    real                  :: frequency                  ! MHz
 +
    character(len=maxstr) :: name                        ! str
 +
  end type station
 +
</pre>
 +
 
 +
It wouldn't be much of a radio unless it had a number of stations.  We can use an (allocatable) array of things of our new derived type to store all the required station information:
 +
 
 +
<pre>
 +
type(station),allocatable,dimension(:)  :: stations    ! array of stations
 +
</pre>
 +
 
 +
I've made the array allocatable for flexibility, or perhaps out of habit.  I guess we could grow it if more stations became availble..
 +
 
 +
After allocating the array (and importantly checking to see whether we hit a problem), all I need to do is fill out the array and print it to the screen.  Note the use of '''%''' in the syntax.
 +
 
 +
We can pass derived-tpes around, into and out of subroutines for example, just like intrinsic types.  This enables us to write very elegant and maintainable programs.  If we discover that we need some more information added to our radio station type, we just add it and the rest of the program can remain unchanged--very handy!
 +
 
 +
Have a go now at writing a subroutine which takes the radio station array as an argument.  Then add something new to the derived type and access it in your subroutine.
  
 
=Modules=
 
=Modules=

Revision as of 15:50, 26 February 2008

'Fortran2: Getting the most from Fortran'

Following On

These notes follow on from the Fortran1 course. You may want to look back at those notes and examples, or if you're feeling confident, just dive in here!

To get the example programs login in to a Linux box and type:

svn co http://source.ggy.bris.ac.uk/subversion-open/fortran2/trunk fortran2

Allocatable Arrays

We'll begin by looking at 'allocatable arrys.

Previously, we declared the size of our arrays when we wrote our code. This is all well and good, and at least we knew where we stood. However, this exact specifiation of the shape of all things can become a painful limitation. Suppose you were writing a program to process a list of University employees. The number of employees will almost certainly vary from one month to the next. So, we can only know the number of records to read in at runtime. This sort of thing starts to crop up a lot when we aim to write more general-purpose, flexible & robust programs.

Fear not! however, as trusty Fortran90 is here to help. In Fortran90 we can write array declarions of the form:

<type>,allocatable,dimension(:) :: my1dAllocatableArray  ! a 1D array of type <type>, which I will give a size to later

Let's go to an example:

cd fortran2/examples/example1

and take a look inside the file allocatable.f90.

In this example, we have a 2D grid and corresponding arrays of longitude and latitude coordinates, the sizes of which we will read from a namelist file at runtime. We will then allocate an appropriate amount of space, fill in the oordinate values according to the number of grid-cells specified in the file and report the whole lot to standard-out. Our arrays are declared with the allocatable attribute:

  real,allocatable,dimension(:)   :: x_coords          ! x-coordinate values
  real,allocatable,dimension(:)   :: y_coords          ! y-coordinate values
  real,allocatable,dimension(:,:) :: data              ! 2D data on grid

We've covered namelists before, so reading the values of the variables nx and ny is straighforward. Once we know these values we can request memory space for our arrays. Note that it is an excellent idea to check whether this request was satisfied. If it were not for some reason--if you request a huge amount of memory, your computer may not be able to oblige--we probably want the program to stop, lest bad things happen!

  allocate(x_coords(nx),stat=errstat)
  if (errstat /= 0) then
     write (*,*) 'ERROR: could not allocate x_coords array'
     stop
  endif

Next is a spot of arrithmetic to fill out the coordinate arrays properly. We want cell-centre coordinates, with longitude running from 0 to 360 degrees and lattitude from -90 to +90 degrees.

So we're done? Almost, but not quite! It is vital that once you're finished with it, you clear up any memory that you allocated earlier. If you don't, bad things are very likely to happen. So the last few lines look like:

  if(allocated(data))       deallocate(data)
  if(allocated(x_coords))   deallocate(x_coords)
  if(allocated(y_coords))   deallocate(y_coords)

Here, we're a bit smarter than the average pup, and we've checked to see if an array has indeed been allocated before deallocating it. It would be unwise to try to deallocate something which had not been allocated in the first place.

So there we are. Sorted. Allocatable arrays. Try varying the values in input.nml first, and then procede to writing a few allocatable arrays of your own.

User Derived Types

Next up, let's look at another very useful feature provided by good old Fortran90---user-derived types:

cd ../example2

Way back at the start of Fortran1 we looked at the half dozen or so intrinsic types offered by Fortran90. It's not so much that this set of types is becoming a limitation (as we found with static array sizes) but more that for a large program things get a bit, well, messy! "Not the end of the world", you may say. And you're right. However, mess does lead to bugs and bugs are a huge problem, so actually messy, spaghetti-like code, is a significant problem that we want to avoid at all costs.

Now, user-derived types offer us something simple, yet potentially extremely powerful--we can group things which are related into a single composite type. That is, we can group all the things that belong together into a single entity and then pass that entity around as work is done by our program. This is a huge bonus when designing and maintaining a reasonably sized program. As we'll find out with growing experience, user-derived types are, pretty much, the best thing since sliced bread!

(User-derived types represent a first step down the road towards object-oriented programming. A step in the right direction in terms of designing programs.)

OK, enough of the spiel. Let's take a look at user-types.f90.

I recently bought a DAB radio and I'm as pleased as punch with it. One of the great features is that you an select a radio station by name, push a button and bingo!, you're tuned in. No more scrolling around the dial trying to get clear reception. That makes it really easy to 'station hop' and find something good on. Now, imagaine we were designed a program to implement a DAB radio. It's natural that we would want to keep the radio station name and tuning frequency (still needed, of course, but only by the machine as it now does the tuning for you) together in a single bundle. They belong together, after all, and are of different intrinsic types--a chacter string and a real number. In the program it looks like:

  type station
     real                  :: frequency                   ! MHz
     character(len=maxstr) :: name                        ! str
  end type station

It wouldn't be much of a radio unless it had a number of stations. We can use an (allocatable) array of things of our new derived type to store all the required station information:

type(station),allocatable,dimension(:)   :: stations    ! array of stations

I've made the array allocatable for flexibility, or perhaps out of habit. I guess we could grow it if more stations became availble..

After allocating the array (and importantly checking to see whether we hit a problem), all I need to do is fill out the array and print it to the screen. Note the use of % in the syntax.

We can pass derived-tpes around, into and out of subroutines for example, just like intrinsic types. This enables us to write very elegant and maintainable programs. If we discover that we need some more information added to our radio station type, we just add it and the rest of the program can remain unchanged--very handy!

Have a go now at writing a subroutine which takes the radio station array as an argument. Then add something new to the derived type and access it in your subroutine.

Modules

Variables and subroutines. Use (with only attributes). Group together related info/routines.

Using separate files

Will do that - JPR

Libraries

Will do this too - JPR

Appendices

Including NetCDF

Have written examples, will write up notes - GW.