<<

IDL for IDLers

IDL for IDLers

An introductory course in programming techniques in IDL

Steve Milan ([email protected]) Department of Physics and Astronomy, University of Leicester

Steve Milan, September 2005 1 IDL for IDLers

IDL for IDLers An introductory course in programming techniques in IDL

Steve Milan ([email protected]) Department of Physics and Astronomy, University of Leicester

0. Introduction · 1. Getting started · 2. IDL · 3. Writing programs: Procedures and Functions · 4. Variables, arrays and matrices · 5. Loops · 6. Decision-making · 7. Input and output · 8. Plotting · 9. A worked example: Pogo · 10. Programming tasks

§0. Introduction

IDL (Interactive Data Language) is very similar to many other programming lan- guages, including , , BASIC, and PASCAL. It contains similar con- structs: variables, loops, logic statements, , etc., though the syntax of the language will at first be unfamiliar and may appear idiosyncratic. However, once one computer language has been mastered it becomes straightforward to understand and program in another language. Instead of being related like English and Japanese, in which the words and grammar are very different, computer languages are rather more related like Cockney and Scouse: the words are all the same, they just sound different. So, if you are familiar with another , chances are you will find IDL similar. Alternatively, if you have not programmed before, learning IDL will give you a head-start in any other language you will learn in the future.

This course is designed, then, to give a taster in some programming techniques which are applicable to all computer languages; it just so happens the language we use will be IDL. Some languages such as C and FORTRAN are relatively prim-and- proper (the Queen’ English of languages) and are very fussy about how their data structures are set up. IDL on the other hand is more flexible and consequently is an easy language in which to get code up and running. In addition, IDL has many built- in graph-plotting facilities, removing from the the burden of creating di- agrams from scratch.

IDL is not only a computer language, it is also a programming and envi- ronment. In other languages you tend to write code and compile it in the computer’s basic (or OS), often a variant of UNIX. Once a program is compiled it is executed or run from the operating system, and once the program has finished execution the user is dumped back in the OS. IDL is different. You enter the IDL environment, and within it write programs, compile them, execute them. This distinc- tion will become clear once you begin the course.

Each chapter of the course (from 2 onwards) contains several simple tasks for you to complete. As you finish each chapter ask the demonstrator to tick off these tasks. The final chapter has several longer tasks for you to attempt; get the demonstrator to tick off on each of these tasks completed.

Steve Milan, September 2005 2 IDL for IDLers

§1. Getting started

IDL has been implemented on several different computer platforms, including the UNIX and LINUX family of operating systems and Windows™. §1.1 gives a brief introduction to UNIX-type operating systems and tells you how to start IDL in them. Ignore this if you are using Windows™. §1.2 gives a brief introduction to IDL in Windows™. Ignore this if you are using UNIX or LINUX. Finally, §1.3 tells you what files you should expect to find in your home directory when you log on to allow you to complete the course. Ignore this at your peril.

§1.1. A quick introduction to UNIX and LINUX When you initially log into a machine and open a shell you will be at the mercy of the operating system (OS), probably UNIX or LINUX (for the purposes of this course they behave the same). You will be greeted with a prompt, maybe of one of the fol- lowing forms, depending on the set-up of the machine:

-3.00$

/home/tom >

(though do not be concerned if it is different) followed by a blinking cursor. You will start in your home directory, which if your username is tom might be called some- thing like /home/tom or /people/tom (the second example of prompt above shows the current directory that you are in; in what follows it is assumed that the prompt is of this form1). This is somewhere to store programs and data, though you might want to create subdirectories to help organise your files. The higher directory, e.g. /home, will contain subdirectories for each of the users registered on the system, of which the subdirectory /home/tom is one. /home is itself a subdirectory of the very highest (or root) directory /, which will contain other subdirectories which store machine-specific information, and with which you should not need to concern your- self. The diagram below shows the structure of a fictitious directory tree which might help you follow the description below.

The fictitious UNIX directory structure described in this section

At the prompt you can enter OS commands. Useful ones include (print working directory name)

1 To set up your prompt to look like this, type (or this could be added to your .profile file): export PS1=’`echo $PWD` >’

Steve Milan, September 2005 3 IDL for IDLers

/home/tom > pwd /home/tom /home/tom >

which gives the current directory you are in (initially your home directory), ls (list)

/home/tom > ls automaton.pro data model /home/tom >

which lists the contents of the current directory. You can specify other directories to list, for instance the root directory, which may look something like:

/home/tom > ls / bin etc lib64 model project software temp boot home lost+found opt qnx srv tftpboot cutlass initrd media people root staged tmp data ion misc people_rsl sbin staging usr dev lib mnt proc selinux sys var /home/tom >

Alternatively, a useful shortcut can be

/home/tom > ls .. bob sue tom

which lists the contents of the directory above the current one, i.e. /home. Even more complex paths can be specified, for instance

/home/tom > ls ../sue

refers to the subdirectory sue in the directory above the current one, i.e. /home/sue.

In the ls of /home/tom (see above) data and model refer to subdirectories (i.e. /home/tom/data and /home/tom/model), while automaton.pro is a pro- gram file. It can sometimes be difficult to distinguish between directories and other files, so ls –l gives more information:

/home/tom > ls -l -rw-r--r-- 1 tom ph_ion 854 Apr 4 2003 automaton.pro drwxr-xr-x 2 tom ph_ion 4096 Apr 12 1999 data drwxr-xr-x 2 tom ph_ion 4096 Aug 25 2000 model /home/tom >

The d in the first column indicates a subdirectory. Other information is contained here, including the date of creation of the file or directory and its size (854 bytes in the case of automaton.pro). To enter a subdirectory use cd (change directory):

/home/tom > cd data /home/tom/data >

would enter the subdirectory data assuming that it was within the current directory (/home/tom), or the complete path can be specified

/home/tom > cd /home/tom/model /home/tom/model >

which enters the directory /home/tom/model directly, irrespective of which direc- tory you are currently in. To make a new directory use mkdir, e.g.:

Steve Milan, September 2005 4 IDL for IDLers

/home/tom > mkdir programs /home/tom > cd programs /home/tom/programs >

It is possible to edit an existing file, or make a new file and edit its contents, with the 2 nedit editor , e.g.:

/home/tom > nedit my_program.pro &

which will open a new window, and allow text to be entered. If the file my_program.pro already exists the existing text can be modified; if my_program.pro does not already exist it will be created once the file is saved. The & is UNIX jargon which means that the nedit window does not have to be closed before new commands can be entered at the prompt.

Copies of existing files can be made using cp:

/home/tom > cp automaton.pro copy_of_automaton.pro

The name of a file can be modified or its location changed with mv (move):

/home/tom > mv automaton.pro robot.pro /home/tom > mv automaton.pro programs/automaton.pro

The first example would change the name of the file automaton.pro to ro- bot.pro, the second would remove the file automaton.pro from the current di- rectory (/home/tom) and place it in the directory /home/tom/programs. Last- ly, unwanted files can be removed with rm:

/home/tom > rm copy_of_automaton.pro

Beware: rm is irreversible!

These UNIX commands should be all that you need to complete this course.

§1.1.1. Starting IDL To start IDL simply type idl at the UNIX prompt:

/home/tom > idl IDL>

The IDL> prompt indicates that IDL is now running and you are within the IDL envi- ronment. Within the IDL environment you can still run UNIX commands by prefac- ing them with a $:

IDL> $ls automaton.pro data model IDL> $nedit my_program.pro &

The rest of this handbook will assume that you are within the IDL environment.

2 The moving cursor writes; and having writ / Moves on: nor all thy piety nor wit / Shall lure back to cancel half a line, / Nor all your tears wash out a word of it. — from the Rubaiyat of Omar Khayyām

Steve Milan, September 2005 5 IDL for IDLers

Finally, to end the IDL session and return to the UNIX operating system type

IDL> exit /home/tom >

§1.2. IDL in ™.

Once you have started IDL, for instance choosing it from the Start menu, you will end up with a window that looks a bit like the following.

Different panels within the window allow you to enter and edit programs, compile and run them, and view the output, either textual or graphical. Most commands are en- tered at the command line, marked “IDL>”, and this is what is referred to in the rest of this course when you see a line such as

IDL> print,2+3

To start editing a new program select the New button (see figure), and enter the pro- gram in the editing panel. Select the Save button to save the file. You can then select the Compile button to compile the program. The Start button will then execute it. This will be covered in more detail in the following sections.

Files can be stored in any folder in the Windows™ directory structure; obviously it is good practice to create a special folder to hold your programs in. If you wish to refer to files in IDL by name (be they program files or data files) then you need to describe them in full. For instance, on my computer the file associated with the “bouncing ball” program found in §6 is C:\Documents and Settings\ets\My Docu- ments\IDL programs\bouncy_ball.pro. If the name contains spaces (as

Steve Milan, September 2005 6 IDL for IDLers

does this one) then you will need to specify it in "quotation marks" within IDL. Hence, to compile the program I would type

IDL> .run "C:\Documents and Settings\ets\My Documents\IDL programs\bouncy_ ball.pro"

Using the Compile button is obviously easier.

§1.3. What you should find in your home directory This course is a combined tutorial, with worked examples, and tasks. A few of the tasks in later sections require the following data files:

house.dat landmarks.dat map.hgt RSPP_staff.dat solar_wind.dat Mountsorrel

If these are not already in your home directory then ask your demonstrator. Alterna- tively, email me ([email protected]) for copies.

Summary This section has provided an introduction to the UNIX commands pwd, ls, cd, cp, mv, rm, and mkdir the nedit editor starting and ending IDL issuing UNIX commands from within IDL the Windows™ IDL environment

Steve Milan, September 2005 7 IDL for IDLers

§2. IDL basics

Being a programming and data analysis environment, it is possible to type language commands at the command line (whenever the IDL> prompt appears) to see immedi- ately how they work. Below is a whistle-stop tour intended to give you a flavour of the syntax of IDL and the sorts of statements that are possible. Type the following lines at the command line to see what happens (it does not matter if you use upper or lower case).

IDL> print,2+3

The print command tries to interpret everything that comes after the comma, and prints the result. In this case the answer is 5. Several items can be separated by commas:

IDL> print,2+3,4*6

Arithmetic operations include +, -, *, /, and ^ (to the power of). Brackets ( and ) can be important for ensuring that arithmetic is done in the correct order.

Task 2.1 Try sums such as 2+3*5 and (2+3)*5 to show that they do not give the same re- sult. Use other examples to show that IDL evaluates * and / before + and – unless brackets are used.

Task 2.2 Experiment to find where ^ in to the order of evaluation.

Task 2.3 Notice also that with these sums IDL does integer arithmetic so that print,3/2 does not give the result you might expect. Show that 3.0/2 gives the correct answer. Why?

IDL> a=3.5 IDL> help,a IDL> print,a*15

The line a=3.5 creates a variable a and assigns a value of 3.5 to it. Because the number is non-integer, IDL decides that a floating point variable is the most appropri- ate type in which to hold it and makes a a floating point variable (variable types are described in more detail in §4). Help is a useful way of finding out what IDL thinks it is up to. In this case, it tells you that the variable a is floating point and tells you what value it currently holds.

IDL> print,!pi

IDL has some system variables which contain useful numbers, identified by the ! pre- fix. !pi is one of these. Another example is !dtor (degrees to radians) which con- tains the value 2π/360.

IDL> print,sin(0.25*!pi) IDL> print,sqrt(2)/2

Steve Milan, September 2005 8 IDL for IDLers

There are a variety of mathematical functions defined in IDL, including trigonometric functions and square root. A list of the more useful ones can be found in the Appen- dix.

Task 2.4 At this point see what happens if you use the up and down arrow keys. The left and right arrow keys and Backspace can be used to modify a line, before pressing Enter. This can save a lot of typing!

IDL> a=’Ganglion’ IDL> help,a IDL> =’infestation’ IDL> c=a+’ ’+b IDL> print,c IDL> print,strmid(c,4,4) IDL> help

IDL can also be used to store and manipulate text or strings of characters. Strings are contained in inverted commas or quotation marks (it does not matter if you use the single quote ’ or double quote ” symbol, but you must be consistent). a=’Ganglion’ stores the word Ganglion in variable a.

Aside: Note that IDL realizes that a must become a string variable type, and changes it accordingly (it was floating point before). IDL is not fussy and is able to change its variable types on the fly; most other languages expect you to declare beforehand what type each variable should be, and this subse- quently becomes set in stone. IDL’s ability to change variable types is a Good Thing in that it does not cramp your style; on the other hand it can be a Bad Thing if you lose track of the type of each of your variables.

Strings can be concatenated using the + operator. Because the variables involved are strings, IDL knows not to try to add them. The strmid command allows you to pick out a bit from the middle of a string, in this case starting from the 4th character in (counting from 0) and selecting the next 4 characters. A list of useful string operators can be found in the Appendix.

Task 2.5 Given that the two variables a and b have been assigned the values a=’Ganglion’ and b=’infestation’, predict the outcome of IDL> print,strmid(a,0,4)+strmid(b,4,3) and check that you were correct.

IDL> a=[0,2,4,6] IDL> help,a IDL> print,a(2) IDL> print,a*10 IDL> a=a*0.5 IDL> print,a

IDL can also hold lists of numbers in arrays. An array can be of any variable type (integer, floating point, string, etc.). There are a variety of ways of creating arrays, as described in §4.2. a=[0,2,4,6] tells IDL to create an array of one dimension with four elements containing the numbers 0, 2, 4, and 6. As all of these numbers are inte- gers, IDL makes the array of integer type; a single array cannot contain different vari- able types.

Steve Milan, September 2005 9 IDL for IDLers

Task 2.6 What do you think would happen if you typed b=[0,2,4.5,6]? Try it, and exam- ine the consequences using print and help.

Individual elements within the array can be accessed by their index number (the posi- tion in the list). Indices start at 0, so a(0) contains (in this example) the value 0, a(1) contains 2, etc. When an array is involved in a mathematical operation, the same operation is applied to each index in the list in turn.

IDL> breakfast=[’sausage’,’bacon’,’eggs’,’beans’,’mushrooms’,’fried bread’] IDL> print,’For my breakfast I want ’,breakfast([1,2,5]) IDL> breakfast(5)=’toast’ IDL> print,’The deep-fryer is broken, so ’,breakfast([1,2,5])

An array can hold a list of strings. Multiple items in an array can be accessed at the same time by providing an array of indices. Individual items in an array can be changed or manipulated without affecting the other elements.

IDL> a=findgen(200) IDL> print,a IDL> plot,a,a*sin(a*0.1)

There are a variety of ways of making arrays (see §4.2). One is to use the findgen (floating point index generator) function. a=findgen(200) produces a one- dimensional floating point array containing the values 0, 1, 2, ... 199. The plot command takes two arrays and plots a graph using the first array as the abscissa (x), the second as the ordinate (y). Try also:

IDL> plot,a*cos(a*0.l),a*sin(a*0.1) IDL> plot,[-1,1,1,-1,-1],[1,1,-1,-1,1],xrange=[-2,2],yrange=[-2,2]

Task 2.7 Change the last line so that the square becomes a house by adding a triangular roof.

IDL> i=5 IDL> if i mod 2 eq 0 then print,i,’ is even’ else print,i,’ is odd’

IDL can make logical decisions using the if...then...else construct. In this case, i mod 2 is evaluated (modulo arithmetic: the remainder after division by 2) and if the result is equal to (eq) 0 the value of i is even, otherwise (else) it must be odd. There are a variety of logical tests, in addition to eq, including ne (not equal to), lt (less than), and ge (greater than or equal to). These are treated in more detail in §6.

Task 2.8 Assign integer values to three variables x, y, and z. Write an if...then statement that checks to see if the variables form a Pythagorean triplet, i.e. that a triangle hav- ing sides of length x, y, and z would be right-angled, assuming z is the hypotenuse.

IDL> for i=0,9 do print,i,’ x 2 =’,i*2 IDL> for i=0,9 do if i mod 2 eq 0 then print,i,’ is even’ else print,i,’ is odd’ IDL> for i=0,9,2 do print,i

Steve Milan, September 2005 10 IDL for IDLers

IDL can be made to execute the same command multiple times using loops. The for...do construct is the easiest way to do this. A loop counter, in these examples the variable i, is given an initial value of 0 and the statement after the do is executed; subsequently the loop counter is incremented by 1 and the statement executed again, and so on. The loop ends when i exceeds the value 9. A third number specifies the increment if it is to be different to 1. Loops will be treated in more detail in §5.

Task 2.9 Write a loop that prints the numbers 100 to 0 backwards.

Finally, note that there is a (very) comprehensive help facility in IDL (this is different from the help command), including reference documentation and user guides. In Windows™ choose Help from the menu bar, or in UNIX type

IDL> ?

The help facility is your friend. Become well acquainted with it.

Summary This section has provided an introduction to integer, floating point and string variables system variables, including !pi and !dtor arithmetic, including +, -, *, /, ^, and mod the sin(), sqrt(), strmid(), and findgen() functions the print and plot statements logic and the if...then...else construct loops and the for...do construct the help statement the help facility, ?

Steve Milan, September 2005 11 IDL for IDLers

§3. Writing programs: Procedures and Functions

In §2 all the statements were typed at the command line. This provides a useful way to test statements. However, for anything other than trivial applications it is necessary to write programs composed of modules, which come in two types: procedures and functions. A program is several lines of commands typed into a file and compiled pri- or to being executed (once or many times). This has two major advantages: (1) you don’t have to keep typing in the same command to perform a computation, and (2) the programs can become more complex than that which could be fitted onto a single command line.

UNIX: As described in §1.1, files can be created and modified using the UNIX ned- it editor (an alternative is vi – but don’t go there...). To start the editor from within IDL, for instance to create or edit a file find_roots.pro, use

IDL> $nedit find_roots.pro &

Remember that save must be selected in nedit before any changes you make in the editor are communicated to the file being edited.

Windows™: As described in §1.2, program files can be created and edited within the IDL environment. The program file must be saved and given a name before it can be compiled and executed.

§3.1. Procedures As an illustration of a simple program, we will write a routine to find the roots of a quadratic equation, ax 2 + bx + c = 0. Such an equation has solutions given by the well-known formula − b ± b 2 − 4ac x = . 2a Crucially, the of the solutions depend on the value inside the square root, known as the discriminant: if b 2 − 4ac > 0 then there are two real roots; if b 2 − 4ac = 0 then there is a single real root; if b 2 − 4ac < 0 then the two roots are complex. First we write a program to determine the nature of the solution, given the three coefficients a, b, and c.

Task 3.1 Create a file called find_roots.pro and enter the following lines of code:

find_roots.pro pro roots,a,b,c

; A program to determine the nature of the solution(s) of a ; quadratic equation of the form a.x^2 + b.x + c = 0

if b^2-4*a*c gt 0 then print,’There are two real roots.’ if b^2-4*a*c eq 0 then print,’There is one real root.’ if b^2-4*a*c lt 0 then print,’There are two complex roots.’

end

Make sure the file is saved. Now type

Steve Milan, September 2005 12 IDL for IDLers

IDL> .run find_roots

or select the Compile button in the Windows™ IDL window. This tells IDL to com- pile the program(s) contained in the file find_roots.pro. There is no need to specify the ending .pro as IDL expects all program files to end with this. If the program compiled correctly you should get the message

% Compiled module: ROOTS.

If there was an error in the code then you will get

% Syntax error.

and a diagnostic telling you where in the program the error was found (including the line number in the file). In this case correct the error and recompile the program.

The file contains one procedure — an IDL program unit — named roots defined by the construct pro name,arguments...end. Following the procedure name there can be a list of variables that IDL expects to be defined when the procedure is executed; when values are passed to a procedure or function in this way they are called arguments. Between the pro and the end are the list of commands that con- stitute the procedure.

The first two lines are a comment explaining what the program does. This is for the benefit of the user: anything on a line following a ; is ignored by IDL.

The following three lines contain if...then statements which test for the nature of the solution.

The procedure can be executed by typing its name at the command line, and supply- ing the required three arguments:

IDL> roots,1,4,3

As the procedure executes this assigns the values 1, 4, and 3, to the variables a, b, and c which are then used in the if...then statements. The response should be

There are two real roots.

Experiment with different values of the arguments to produce the other two possible outcomes. We can now modify the program to calculate the roots, having determined their nature.

Task 3.2 Modify the contents of the find_roots.pro file as follows (remembering that cut and paste can make your life much easier!):

find_roots.pro pro roots,a,b,c

; A program to determine the nature of the solution(s) of a ; quadratic equation of the form a.x^2 + b.x + c = 0, ; and then calculate them

if b^2-4*a*c gt 0 then real_roots,a,b,c if b^2-4*a*c eq 0 then single_root,a,b,c if b^2-4*a*c lt 0 then complex_roots,a,b,c

Steve Milan, September 2005 13 IDL for IDLers

end

;------

pro real_roots,a,b,c

print,’There are two real roots.’ x_pos=(-b+sqrt(b^2-4*a*c))/(2*a) x_neg=(-b-sqrt(b^2-4*a*c))/(2*a) print,x_pos,x_neg

end

;------

pro single_root,a,b,c

print,’There is one real root.’ x=-b/(2*a) print,x

end

;------

pro complex_roots,a,b,c

; Convert b^2-4*a*c into a complex variable type. ; If the argument supplied to the sqrt is complex then it ; calculates the complex solution.

print,’There are two complex roots.’ d=complex(b^2-4*a*c,0) ; make the discriminant complex... x_pos=(-b+sqrt(d))/(2*a) ; ...so the solutions are complex x_neg=(-b-sqrt(d))/(2*a) print,x_pos,x_neg

end

There are now four procedures contained within the single file, and when compiled (.run find_roots) the following message will appear:

% Compiled module: ROOTS. % Compiled module: REAL_ROOTS. % Compiled module: SINGLE_ROOT. % Compiled module: COMPLEX_ROOTS.

Each procedure could have been contained in a separate file, and each file compiled in turn. However, this way all the associated code is kept together in one place. The lines of dashes between procedures are entirely superfluous, but help to see where one procedure ends and another starts (esp. in large files). Note also that comments can appear on the same line as a command; everything after the ; is ignored. Now

IDL> roots,1,4,3 There are two real roots. -1.00000 -3.00000

results in the nature of the solution being determined within the roots procedure, the if...then statements then sending the values of a, b, and c to a separate proce- dure (real_roots, single_root, or complex_roots) that evaluates the root(s) appropriately. Procedures are called from within another procedure by treat- ing them just like an IDL statement, i.e. just giving their name, followed by any re- quired arguments.

Steve Milan, September 2005 14 IDL for IDLers

The computation of the single or pair of real roots is straightforward. The computa- tion of the complex roots uses IDL’s ability to do arithmetic with complex numbers. The statement creates a variable of complex type, d=complex(b^22 -4*a*c,0) d with a real part equal to b -4ac and an imaginary part of 0. When the sqrt function is called, it determines that the variable d is complex, so it provides a complex solu- tion. Both variables x_pos and x_neg are then complex. IDL displays complex numbers in brackets:

IDL> roots,1,2,3 There are two complex roots. ( -1.00000, 1.41421)( -1.00000, -1.41421)

The solutions in this case are −1± i 2 .

Note also, that individual procedures can be called from the command line in their own right. For instance, the following is perfectly valid:

IDL> real_roots,1,4,3 There are two real roots. -1.00000 -3.00000

However, supplying values of a, b, and c for which b2-4ac < 0 generates an error:

IDL> real_roots,1,2,3 There are two real roots. NaN NaN % Program caused arithmetic error: Floating illegal operand

NaN is IDL code for not a number: there is no solution for the square root of a nega- tive number unless it is treated as complex. In real_roots all the numbers are treated as real, so the procedure fails. Alternatively, complex_roots could be called for all cases, but the solutions would always be complex, even if they did not need to be:

IDL> complex_roots,1,2,1 There are two complex roots. ( -1.00000, 0.00000)( -1.00000, 0.00000)

Task 3.3 Add an additional procedure to the end of find_roots.pro that if supplied argu- ments a, b, and c determines if they form a Diophantine quadratic, that is one in which the solutions are integers. Hint: to check that n is an integer, test if fix(n) eq n, where the function fix(n) returns the integer part of n.

Aside: There is seldom one right way to write a program; there is another way in which find_roots.pro could have been written. Given a, b, and c it could use complex arithmetic to find two complex roots x_pos and x_neg. It could test to see if these were equal, in which case there is only a single root. It could test if the roots were really complex or in fact were real, in which case imaginary(x_pos), which returns the imaginary part of x_pos, would be 0. The real (or floating point) part of a complex number is returned by float(x_pos). It is best when writing a program to think of several ways to tackle the problem at hand; the first method you think of may not be the most appropriate.

Steve Milan, September 2005 15 IDL for IDLers

§3.2. Functions It is possible to write your own functions, much like sin() or sqrt(), which take an argument and return a value. These could be straight-forward mathematical func- tions, for instance:

Task 3.4 Create a file entitled cosine_rule.pro and enter the following code.

cosine_rule.pro function cosine_rule,b,c,angle_A

; A function to the cosine rule to determine the ; length of the third side of a triangle a, given the lengths ; of the other two sides b and c and the opposite angle ; angle_A (in degrees)

a=sqrt(b^2+c^2-2*b*c*cos(angle_A*!dtor)) return,a end

Now the construct function name,arguments...end defines the function and the return statement specifies the value to return. As before, to compile this program (which in this case consists of a single function) type:

IDL> .run cosine_rule

The function is executed as if it were a normal IDL function (though it can have sev- eral arguments, unlike, for example, sin()):

IDL> length=cosine_rule(3.5,4.0,135) IDL> print,length 6.93174

Functions return a value which can then be used in subsequent calculations. This is different from the operation of the previous procedures which printed their re- sults but did not return the values.

Aside: Note that IDL trigonometric functions assume that their arguments are given in radians. The cosine_rule function above was written to expect angle_A to be specified in degrees, so the sys- tem variable !dtor (which has a value of 2π/360) is used to convert to radians.

Functions can also involve more complex logic, as shown in the next example.

date.pro function day_number,year,month,day

; A function to determine the day number (day of year) ; of the specified date. Strictly only correct for ; Gregorian calendar.

doy_month=[1,32,60,91,121,152,182,213,244,274,305,335]

if ((year mod 4 eq 0) and (year mod 100 ne 0)) or year mod 400 eq 0 then $ doy_month(2:11)=doy_month(2:11)+1

doy=doy_month(month-1)+day-1

return,doy

Steve Milan, September 2005 16 IDL for IDLers

end

This function determines the day of the year of a specified date. The array doy_month specifies the day of the year of the 1st of each month assuming the year is not a leap year. The if...then statement determines if year is a leap year or not: it is a leap year if year is divisible by 4, but not if it is divisible by 100 unless it is also divisible by 400. Use of logical operators such as and and or will be dealt with in more detail in §6. If it is a leap year then February has 29 days instead of 28, so doy_month from March onwards must be incremented by one. Note that indi- vidual lines of code in IDL can be of any length, but to aid readability they can be split: the $ indicates that the command continues on the following line. I have in- dented the second line to make it clear to the reader (the computer doesn’t care) that this second line is not a new command in itself. Finally, the day of year (doy) of the specified date is calculated by adding day to doy_month for the appropriate month (why month-1 ?) and subtracting 1. The return statement indicates that it is this value which is to be returned by the function.

IDL> .run date % Compiled module: DAY_NUMBER. IDL> print,day_number(1999,12,31),day_number(2000,12,31) 365 366

Task 3.5 Add a second function to date.pro that, given a year, month, and day, calculates the week number. Use the already defined day_number() function in the calcula- tion.

Aside: One of the commonest errors encountered using functions occurs when a function is called, but that func- tion hasn’t previously been compiled. In this case, a statement of the form

IDL> print,day_number(2000,12,31)

would be interpreted (incorrectly) by IDL as a request to print a single element of a three-dimensional array called day_number. In this case, the following error would be produced:

% Variable is undefined: DAY_NUMBER. % Execution halted as :$MAIN$

§3.3. Keywords Keywords are another means of passing arguments in IDL. We will not learn how to use keywords ourselves in programs, but many IDL commands (for instance, plot) make extensive use of keywords, and so they are introduced here.

Keywords are used when a function or procedure has a default action, but this action is to be overridden. For instance, the plot command plots graphs by default in col- our 1 (this will be treated in more detail in §8). However, by specifying the keyword color (IDL is American) in the plot command, this default operation is changed:

IDL> plot,x,y IDL> plot,x,y,color=10

Keywords can often be abbreviated, so col=10 would also be valid. Lastly, you will meet the following syntax:

IDL> plot,x,y,/ylog

Steve Milan, September 2005 17 IDL for IDLers

The default operation of plot is to use linear axes, but this can be overridden by specifying the /ylog keyword. This has exactly the same meaning as, but is short- hand for, ylog=1. This syntax is usually used when a keyword has an on/off effect.

Summary This section has introduced programs saved as files the UNIX nedit text editor procedures and functions the pro name,arguments...end construct the function name,arguments...return...end construct keywords the use of $ in a program to split a line of code over two lines of a file the use of ; to denote comments the complex variable type the fix(), float(), imaginary() functions

Steve Milan, September 2005 18 IDL for IDLers

§4. Variables, arrays and matrices

There are a variety of variable types in IDL (most of which are common to other lan- guages, also) including byte, integer, long integer, floating point, double precision, complex, double precision complex, and string. These are used to store numbers or text characters (strings). Computers cannot store numbers to infinite precision. Dif- ferent variable types store numbers to different precisions, though the greater the pre- cision required the more memory that is necessary to store the extra information. Hence, precision and memory requirements can be a trade-off; however, you are un- likely to meet this problem during the present course.

§4.1. Variable types Byte, integer, and long integer variables store integer numbers, but vary in the amount of memory used and consequently the size of the numbers that can be stored:

type bits storage byte 8 0 to 255 integer 16 -32768 to 32767 long integer 32 ~ ±2×109

Byte variables are usually reserved for specialist purposes. The most common integer type used (and the default) is the integer, though long integers are used when large numbers are needed. There are several ways to assign values to integer and long inte- ger variables:

IDL> a=2 IDL> b=long(2) IDL> b=2l ; note this is a number 2 followed by a letter L

In the first case, a would be made an integer type variable; in the other two cases b would be made a long integer variable.

Floating point and double precision types store real (i.e. non-integer) numbers as an integer and an exponent, for example the number 12.34567 would be stored as 1234567×10-5 where 1234567 is the integer part and -5 is the exponent. In this way fractional values, both very large and very small, can be stored, but only with limited accuracy, i.e. to a limited number of significant digits. Double precision variables differ from floating point in that they hold more significant digits. There are various ways to assign a value to a floating point variable:

IDL> a=2. IDL> a=2.0 IDL> a=float(2)

are all equivalent. The function float() converts from other number types to float- ing point. Alternatively, exponential notation can be used:

IDL> mass_el=9.1095e-31

-31 assigns the value 9.1095×10 to the variable mass_el. Double precision variables can be assigned as follows:

IDL> a=2d IDL> a=double(2) IDL> mass_el=9.1095d-31

Steve Milan, September 2005 19 IDL for IDLers

Complex and double precision complex variables are the same as floating point and double precision, except they store two parts, a real and an imaginary, and when they are involved in calculations complex arithmetic is used.

IDL> i=complex(0,1) IDL> i=dcomplex(0,1)

To return the real or imaginary parts of a complex number as a floating point value use float() and imaginary():

IDL> a=complex(4,-5) IDL> print,a ( 4.00000, -5.00000) IDL> print,float(a) 4.00000 IDL> print,imaginary(a) -5.00000

Floating point and double precision variables can be converted to integer or long inte- ger type with fix() and long(), respectively, for instance:

IDL> print,fix(137.03599976) 137

As mentioned in task 3.3, testing that fix(n) eq n can be a useful way to deter- mine if the floating point variable n contains an integer value. Fix() returns the in- teger part of the its argument, not the rounded value, so for example fix(2.9) gives 2. Other functions for rounding values are round(), floor(), and ceil() (ceiling).

Task 4.1 Experiment with fix(), round(), floor(), and ceil() to determine how they work; pick a range of positive and negative values to test.

When doing arithmetic with different variable types, IDL tries to make a sensible de- cision as to the type of number that should be generated. For instance, involving two integers the result is integer (2*2=4), even if division is used (1/2=0). Between an integer and a floating point, the result is floating point (2*2.25=4.5). If a complex number is involved in the calculation, the result is complex too.

Task 4.2 Determine results of the following expressions, including the type of number that is returned (this is why help is used instead of print); try to work out what the an- swer will be before you try them. If the result is unexpected, can you work out why you get the result you do?

IDL> help,2*20 IDL> help,2l*20 ; note the L IDL> help,200*200 IDL> help,2*1e-9 IDL> help,sqrt(double(4)) IDL> help,10^(1/2),10^0.5 IDL> help,exp(complex(0,!pi))

x iπ The function exp(x) calculates e , so exp(complex(0,!pi)) finds e . You should be able to determine what the exact result should be; the value that IDL returns

Steve Milan, September 2005 20 IDL for IDLers

will not be exact, giving an idea of the inaccuracy that can result in complicated cal- culations (due to rounding errors, etc.).

The last type of variable is the string, which can contain 0 or more alpha-numeric characters; the string is assigned between quotation marks. If there are 0 characters in the string it is said to be empty. There are a variety of functions in IDL to manipulate strings.

IDL> full_name=’Kylie Minogue’ IDL> print,strlen(full_name) IDL> names=str_sep(full_name,’ ’) see footnote 3 if this generates an error IDL> print,names(1) IDL> print,strlen(names) IDL> if names(0) eq ’’ then print,’Empty string’

3 The strlen() function returns the length of the string. Str_sep() separates the string into its constituent bits, using the specified character as the delimiter, in this case a space character. In the example, there are two bits to the string (i.e. there is one space) and so the names variable is made into a string array containing two elements. The last line checks to see if the first element of the array is empty (there is no charac- ter between the two quote marks in the if statement); an alternative method of testing this would be to see if strlen(names(0)) eq 0. Of course, in the present ex- ample names(0) is not empty. There are a variety of other string functions (we have already met strmid() in §2), of which more details can be found in the Ap- pendix. Lastly, a brief word about arithmetic with strings: two strings can be concat- enated with + as we saw in §2, but if a string becomes involved in arithmetic with a number IDL tries to interpret the string as a number. The next few examples should make clear how this works:

IDL> print,’123’+’456’ IDL> print,’123’+456 IDL> print,’gusset’+456

The last example will generate an error as ’gusset’ clearly cannot be interpreted as a number. Another way to get a value out of a string would be to use fix(’123’) or float(’123’) which will convert to integer and floating point types, respec- tively.

§4.2. Arrays Each variable can contain not only one value, but a list of values known as an array. Arrays can be of one or more dimensions: a one-dimensional array might hold the time-series of the number of people drinking in the Redfearn over the course of an afternoon, a two-dimensional array spot-heights describing the topography of Britain, a three-dimensional array the potential in a region of space, etc.

§4.2.1. Array assignment and manipulation There are a variety of ways to assign arrays. The expression

IDL> a=intarr(100)

3 Depending on the implementation of IDL you are using, you may need to use the syntax IDL> names=strsplit(full_name,’ ’,/extract)

Steve Milan, September 2005 21 IDL for IDLers

assigns a one-dimensional array of integers, containing 100 elements to the variable a. Initially, all 100 elements will be set to a value of 0.

IDL> a=intarr(100,100)

assigns a 2-D array of 10000 elements to the variable a. Up to 8 dimensions can be specified. Similar functions lonarr(), fltarr(), dblarr(), complex- arr(), strarr() allow other array types to be assigned. Once the array is as- signed, individual elements can be changed by specifying the indices of the element:

IDL> a=fltarr(2,2) IDL> a(0,0)=1 IDL> a(1,1)=2 IDL> a(1,0)=10

This produces an array containing the following values: 1.0 10.0 a =   ; 0.0 2.0  for obvious reasons arrays can act as matrices, as will be covered in §4.4.

We have already met another way of specifying arrays (in §2), viz.:

IDL> c=[0,1,2,3,4,3,2,1,0]

assigns a 1-D array of 9 integer elements. 2-D arrays can assigned in this way too. To assign a as before:

IDL> a=[[1.0,10.0],[0.0,2.0]]

Multiple elements can be assigned or manipulated at one time:

IDL> b=fltarr(4,2) IDL> b(*,0)=1

would produce an array in which all of the top row of elements equal 1.0 and the bot- tom row equal 0.0 (i.e. unchanged). The * indicates that all elements of that dimen- sion are affected. Alternatively,

IDL> b=fltarr(4,2) IDL> b(1:2,0)=1

would produce an array in which the two middle elements of the top row equal 1.0. The n:m indicate the start and end elements of the dimension to be changed. The same could be achieved with

IDL> b=fltarr(4,2) IDL> b([1,2],0)=1

where [1,2] specifies an array containing the two indices to be changed.

We finish with the useful function indgen() (index generator) which allows an ar- ray to be assigned and filled with consecutive integers:

IDL> a=indgen(4) IDL> print,a 0 1 2 3 IDL> print,a*2.5 0.00000 2.50000 5.00000 7.50000

Steve Milan, September 2005 22 IDL for IDLers

Other variants exist, such as findgen() which results in a floating point array which contains the same values.

IDL is very good at processing arrays. It allows very simple instructions to perform many simultaneous calculations. The array a had 4 elements so a*2.5 results also in an array with 4 elements. Another example:

IDL> a=2*!pi*indgen(100)/99

produces an array containing 100 equally-spaced values between 0 and 2π. The indgen(100) function generates an array containing values between 0 and 99, and each element in the array is then multiplied by 2π/99. Such an array could be used to plot a sine wave in 100 steps:

IDL> plot,a,sin(a),psym=-1

Notice that as a was an array of 100 elements, the function sin(a) also produces an equally-sized array.

Task 4.3 Plot the arctangent of x as a function of x in 40 steps in the interval -10 < x < 10. Use the atan() function.

Task 4.4 Write a function interp(x0,x1,n) which returns an array containing n elements which vary linearly in value from x0 to x1.

Arrays can be manipulated with for...do loops. For example, the following code calculates the first n numbers in the Fibonacci series: 0, 1, 1, 2, 3, 5, 8,... in which each number is the sum of the previous two.

fibonacci.pro pro fibonacci,n

; make an array the correct size to hold the series fib=lonarr(n)

; prime the series by setting the first two numbers to 0 and 1 fib(0:1)=[0,1]

; calculate each successive number from the previous two for i=2,n-1 do fib(i)=fib(i-2)+fib(i-1)

print,fib

end

The Fibonacci series appears throughout nature (e.g. in the rate at which animals breed, the arrangements of seeds on flowering plants, and the shapes of spiral sea shells). One of its important properties is that the ratio between pairs of numbers in the series tends towards the Golden ratio (also known as the Golden section or num- ber), which has been revered by artists through the ages (including Da Vinci) as the ratio of the sides of the Golden rectangle, the rectangle with the most aesthetically pleasing proportions; this number is also found ubiquitously in nature.

Steve Milan, September 2005 23 IDL for IDLers

Task 4.5 Modify fibonacci.pro such that it calculates and prints the ratios between all n-1 successive pairs in the Fibonacci series, to determine the value of the Golden ratio.

Aside: for more information on the properties of the Fibonacci series and the Golden ratio see: http://www.mcs.surrey.ac.uk/Personal/R.Knott/Fibonacci/fib.html

§4.2.2. The n_elements() function To find the number of elements in an array use the function n_elements():

IDL> menu=[’king prawn’,’chicken’,’pork’,’roast beef’] IDL> for i=0,n_elements(menu)-1 do print,i+41,’ ’,menu(i)+’ chow mein’ 41 king prawn chow mein 42 chicken chow mein 43 pork chow mein 44 roast beef chow mein

Why is 1 subtracted from n_elements(menu) in the for loop? Using this con- struction it is easy to add additional items to the menu array without having to change the for loop:

IDL> menu=[’king prawn’,’chicken’,’pork’,’roast beef’,’spicy vegetable’] IDL> for i=0,n_elements(menu)-1 do print,i+41,’ ’,menu(i)+’ chow mein’ 41 king prawn chow mein 42 chicken chow mein 43 pork chow mein 44 roast beef chow mein 45 spicy vegetable chow mein

§4.2.3. The where(), max() and min() functions It can be useful to be able to select elements from an array based on some predeter- mined criteria. The where() function allows this:

IDL> wine_list=[’Chateau Cowley’,’Barstows End Bin’,’Raines Reserve’,’Vin de Wynn’] IDL> wine_price=[7.99,6.99,4.99,2.49] IDL> cheap=where(wine_price le 5.00,number_cheap) IDL> print,cheap 2 3 IDL> print,number_cheap 2 IDL> print,’Wines I can afford are: ’,wine_list(cheap) Wines I can afford are: Raines Reserve Vin de table Wynn

In this example the array wine_list holds the names of four wines, and the array wine_price holds the corresponding cost per bottle. The cheap= where(wine_price le 5.00) statement returns the indices of elements in the array wine_price that are le (less than or equal to) 5.00 and places them in an ar- ray called cheap. As two elements of wine_price match the criterion, the array cheap has two elements with values of 2 and 3, the indices of the elements in wine_price that are affordable (though not necessarily drinkable). Although the names of the wines are stored in a different array from wine_price, corresponding names and prices are located at the same indices in the two arrays, so the list of af- fordable wines can be accessed with wine_list(cheap). The variable num- ber_cheap is included as an argument in the where() function (this is optional),

Steve Milan, September 2005 24 IDL for IDLers

which returns from the function containing the number of items that matched the search criterion. Another way to determine this value would be with n_elements(cheap).

IDL> min_price=min(wine_price,cheapest) IDL> print,’Cheapest wine is ’,wine_list(cheapest),’, costing £’,min_price Cheapest wine is Vin de table Wynn, costing £ 2.49000

The function min() determines the lowest value in an array, in this example wine_price. Hence, min_price is set to the value 2.49. The variable cheap- est (optional) contains the index of the minimum value, so in this case is set to 3, the index of Vin de table Wynn. There is a corresponding max() function.

Task 4.6 The printers of the labels for the bottles of wine have realized that the labels are only wide enough to accommodate 15 characters (sorry, a bit contrived this): write a state- ment using where() which lists those wines with names that are too long to fit. The alcohol-by-volume of the four wines is 14, 17.5, 11, and 12%, respectively. How would you determine the wine which gives you the most alcohol for your money? The Offie usually stocks all four wines, but at present has 32, 54, 13, and 0 bottles of each in stock, respectively. Create an array alc which gives the alcohol content of each wine and an array stock which lists the number of bottles in stock. Write a statement that determines which of the available wines exceeds the acceptable 2.5%- per-£ level (you will need an and operator in the where() function).

§4.3. Arrays as matrices As mentioned in §4.2.1, arrays can act as matrices, and IDL has multiplication built-in. Try the following:

IDL> a=[[1,2],[3,4]] IDL> b=[[10,10],[20,20]] IDL> print,a*b

a and b are two 2×2 arrays, and a*b does standard multiplication, IDL multiplying each element of the first array by its corresponding value in the second array. How- ever,

IDL> print,a##b

does proper matrix multiplication. An example might be the use of transformation matrices to manipulate an the coordinates of an object. For instance, if an object is described by a set of (x, y) coordinates in the X-Y plane, then the following expres- sions expand the coordinates by a factor of two and rotate them through an angle θ, respectively: 2 0 x   2x   cosθ sinθ  x   x cosθ + y sinθ     =   ,    =   . 0 2 y 2y − sinθ cosθ  y − xsinθ + y cosθ  This is illustrated in the following example:

house.pro pro house

; Create a square plot window window,0,xsize=500,ysize=500

Steve Milan, September 2005 25 IDL for IDLers

; house contains the coordinates describing the outline of a house house=[[-1,1,1,-1,-1,0,1],[1,1,-1,-1,1,2,1]]

; plot out the untransformed house plot,house(*,0),house(*,1),xrange=[-4,4],yrange=[-4,4] stop

; define two transformation matrices scl=[[2,0],[0,2]] angle=!pi/2 rot=[[cos(angle),sin(angle)],[-sin(angle),cos(angle)]]

; plot the house rotated new_house=rot##house plot,new_house(*,0),new_house(*,1),xrange=[-4,4],yrange=[-4,4] stop

; plot the house rotated and expanded new_house=scl##rot##house plot,new_house(*,0),new_house(*,1),xrange=[-4,4],yrange=[-4,4] end

The window statement opens a plotting window (there can be several, this will be number 0) 500×500 pixels in size. After this you should be able to follow the opera- tion of the program. The stop command will be unfamiliar: this temporarily halts the operation of the procedure and returns the user to the command line. To continue operation use the

IDL> .continue command (this can be abbreviated to .c).

An object can be sheared (slanted) in the X or Y directions with the following matri- ces: 1 k  1 0   or  . 0 1 k 1

Task 4.7 Modify house.pro so that in addition to plotting the house scaled and rotated, it also plots the house leaning (sheared) over at 45°. You will have to first have to ex- pand 1 k  x  1 0 x     and    0 1 y k 1 y to see how to achieve this.

Summary This section has introduced: byte, integer, long integer, floating point, double precision, complex and string variable types the functions fix(), long(), float(), imaginary(), complex(), round(), floor(), ceiling() the use of l, e, and d to describe long, floating and double precision numbers the function exp()

Steve Milan, September 2005 26 IDL for IDLers

intarr() and similar functions indgen() and similar functions the strlen(), str_sep(), and strsplit() functions the n_elements() and where() functions the matrix ## operator stop and .continue

Steve Milan, September 2005 27 IDL for IDLers

§5. Loops

We have already met the for...do construct. We will now describe the for...do begin...endfor construct and then other loop types.

§5.1. for...do begin...endfor Previously, we have used commands of the following type:

IDL> for i=0,10,2 do print,i

The for statement first assigns a new variable i with the value 0. It then executes the statement after the do, i.e. print,i. It then adds the value 2 to i, and executes the print statement again. This continues until i exceeds a value of 10. If the third value following the for statement is omitted (i.e. the 2) then an increment of 1 is as- sumed. Negative increments can be specified if the loop is to count backwards.

Aside: In the above case, i is made an integer variable because the first number is of integer type, i.e. it is written 0 instead of 0.0 for instance. The following example would loop indefinitely with i being equal to 0 at each step – can you see why?

IDL> for i=0,10,0.5 do print,i

For this to work properly, it should be modified to:

IDL> for i=0.0,10.0,0.5 do print,i

This construction is useful if only a single statement is to be repeated in the loop. However, programs can be more complex than this:

seven_ages_of_man.pro pro seven_ages_of_man

print,’All the world’’s a stage,’ print,’And all the men and women merely players,’ print,’They have their exits and entrances,’ print,’And one man in his time plays many parts,’ print,’His acts being seven ages.’

for age=0,69 do begin print,age,’ years old:’ if age lt 10 then print,’Infant.’ if age ge 10 and age lt 20 then print,’Childhood.’ if age ge 20 and age lt 30 then print,’Lover.’ if age ge 30 and age lt 40 then print,’Soldier.’ if age ge 40 and age lt 50 then print,’Adult.’ if age ge 50 and age lt 60 then print,’Old age.’ if age ge 60 then print,’Senility.’ endfor

end

In this case, several statements are contained within the loop, the start and end of what is to be executed at each step of the loop being defined by the begin...endfor construct; I have indented the statements between the begin and the endfor to make it clear what is inside the loop. Loops can also be nested i.e. one loop can occur within another:

Steve Milan, September 2005 28 IDL for IDLers

counting.pro pro count_decimal

for i=0,9 do begin for j=0,9 do begin print,i,j endfor endfor

end

;------

pro count_binary

for a=0,1 do begin for b=0,1 do begin for c=0,1 do begin for d=0,1 do begin dec=a*8+b*4+c*2+d print,a,b,c,d,dec endfor endfor endfor endfor

end

In the case of count_decimal the program achieves the trivial end of counting from 0 to 99 by having a loop that executes 10 times inside a loop that itself executes 10 times. The second example is more interesting in that this cycles through all the possible four-digit binary numbers and calculates their decimal equivalents.

Task 5.1 Add an additional procedure to counting.pro to print out the decimal equivalents of all the possible 3-digit octal (base 8) numbers.

Loops can be used to determine what happens if a process is repeated several times. An example might be rolling a pair of dice a certain number of times. The function randomu(seed) (random uniform) produces a floating point number uniformly distributed between 0.0 and 0.999... (in other words, each fractional value is equally likely); the argument seed is used by the random number generator within IDL, but the user does not need to set it nor worry about the value it is set to. To simulate the roll of a die we could use spots=floor(randomu(seed)*6+1), which would give a number between 1 and 6, with each number having an equal probability. Hence the sum of the roll of two dice would be

spots=floor(randomu(seed)*6+1)+floor(randomu(seed)*6+1)

With two dice it is possible to roll values between 2 and 12; but how are the values distributed?

Task 5.2 Write a program to roll a pair of dice 10000 times and form an occurrence distribution of the number of spots rolled. What is the effect of rolling just a few or many times?

Steve Milan, September 2005 29 IDL for IDLers

Hint: To form an occurrence distribution, create an array with enough elements to contain all the possible out- comes; in this case there are 11:

occ=intarr(11)

The after each throw of the dice increment the occurrence bin corresponding to the number of spots

occ(spots-2)=occ(spots-2)+1

After n rolls the array occ holds the occurrence distribution; it effectively counts the number of times each possi- bility arose. Why, in this particular example, have I used spots-2 as the index of the array occ?

§5.2. while...do begin...endwhile For...do loops are useful if you know in advance how many times you want the loop to execute. If the number of loops necessary to achieve a certain result is not known, then while...do loops can be used.

falling.pro pro falling

height=10.0 vel=0.0 g=-9.81 dt=0.01 steps=0

while height gt 0 do begin vel=vel+g*dt height=height+vel*dt steps=steps+1 endwhile

print,’The vase took ’,dt*steps,’ seconds to fall to the ground’ print,’Its final velocity was ’,vel,’ m/s’

end

This routine calculates (approximately) the time taken for a vase to fall to the ground from an initial of 10 m. Its velocity is initially set to 0 m s-1 and the height -2 vel acceleration due to gravity g to 9.81 m s . The routine uses the method of nu- merical integration to trace the motion of the vase. In this method, the position and velocity of the vase are calculated in a series of steps, the time-step dt being set in this example to 0.01 s (half a jiffy). The number of time-steps taken for the vase to reach the ground is counted in the variable steps, which is initially set to 0 (such a variable that serves this function is often called a counter). The while...do state- ment tests at each step if the vase is still above the ground (height gt 0); if so the commands between the begin...endwhile are executed. These a) determine the new velocity, which is incremented by the acceleration multiplied by the time-step, b) determine the new height, which is incremented by the velocity multiplied by the time-step, and c) increments the number of steps that have taken place. The time tak- en for the vase to fall to the ground is then the number of steps taken multiplied by the time-step.

Aside: of course, this example is somewhat trivial and the Euler method is not necessary to solve the problem as the time taken to fall could be calculated analytically from

Steve Milan, September 2005 30 IDL for IDLers

1 2 s = ut + 2 at , which in this case would reduce to t = 2s g . This is because the acceleration is uniform with time. However, in situations in which the acceleration varies with time (e.g. two or more bodies orbiting each other, see task 10.3) analytical solutions can be difficult to find.

Aside: note that variables do not explicitly have physical units associated with them. It is up to the programmer to decide on the units associated with each variable and to make sure that the way the variables are combined remains physical. For instance the following statement is perfectly legal as an IDL command:

IDL> s=u*t+0.5*a*t but is incorrect if it is meant to represent dynamical motion.

Task 5.3 A crazed physics student hatches a cunning plan to reach the Moon. She takes a (very large) piece of paper and tears it in half. She makes a pile from the two pieces of pa- per and then tears in half again, again stacking the two resulting piles. She reasons that if she repeats this enough times the pile will reach to the Moon. Write a simple program that uses a while...do loop to determine the number of repetitions that are necessary if the paper is 0.1 mm thick and the average distance to the moon is 3.8×108 m.

§5.3. goto The goto command is almost universally reviled in “good programming” circles, as it can make programs very difficult to follow and understand; however, there are oc- casions when it can be useful if used judiciously. The syntax of goto in IDL is:

... jump_to_here: ... goto,jump_to_here ...

A label is defined by a name followed by a colon. When the goto command exe- cutes it looks for the specified label and jumps to that section of code. This example defines an infinite loop: the program will repeatedly execute the commands between the label and the goto statement. For the program to ever leave the loop, there could be an if...then return statement within the loop that would jump out of the procedure when a certain criterion had been met. If a program ever gets caught in an infinite loop (or to stop operation at any time) press ctrl-C to break out of it. goto, §6

Summary This section has introduced: the for...do begin...endfor construction the while...do begin...endwhile construction nested loops forming occurrence distributions the Euler method of numerical integration ctrl-C key combination

Steve Milan, September 2005 31 IDL for IDLers

§6. Decision-making IDL has a number of decision-making constructs. We have already met if...then, and while...do also makes logical decisions about whether to con- tinue looping. In this chapter, we briefly examine decision-making in more detail.

§6.1. Logic The commonly used logical operators are

eq equal to gt greater than ge greater than or equal to lt less than le less than or equal to ne not equal to

Also used are and, or, and not. These are used in conjunction with the if...then, case...of, and while...do constructs to make decisions in IDL.

§6.2. if...then begin...endif else...endelse So far we have encountered if...then...else statements which choose be- tween one of two alternative statements:

IDL> if a gt b then print,’a > b’ else print,’b >/= a’ IDL> if Mstar/Msun le 1.44 then star=’White Dwarf’ else star=’Neutron star’

However, as with the loops we saw in the last section, it is possible to have several statements in each clause: bouncing_ball.pro pro bouncing_ball x=0.0 & y=10.0 vx=1.0 & vy=10.0 g=-9.81 coeff=0.9 dt=0.01 steps=0 plot,[0],[0],/nodata,xrange=[0,20],yrange=[0,15],xtitle=’x’,ytitle=’y’ while x lt 20 do begin

vx=vx+0*dt vy=vy+g*dt

x=x+vx*dt y=y+vy*dt

if y le 0 then begin vy=-vy*coeff y=0 print,’Bounce at ’,steps*dt,’ s’ endif

oplot,[x],[y],psym=3

steps=steps+1

Steve Milan, September 2005 32 IDL for IDLers

endwhile end

Note the use of the & symbol which allows multiple instructions on a single line.

This program extends the Euler integration method of falling.pro to now trace the path of a rubber ball in both x and y dimensions as it bounces against the ground, given an initial height of y = 10 m and an initial velocity -1 -1 with components vx = 1 m s and vy = 10 m s . The if...then statement tests to see if the ball has hit the ground (y < 0) and if so several things must be done: a) the vy component of the ball is reversed so that it bounces, but is multiplied by a factor less than 1 (the coefficient of resti- tution) as the ball is not perfectly elastic; b) it resets the

On the rebound height of the ball to ground level; c) it prints out the time at which the bounce occurs. The required instructions are contained between the begin and endif of the if statement.

Task 6.1 Modify bouncing_ball.pro with the addition of an if...then statement that detects when the top of each bounce is reached (check to see whether vy changes from positive to negative in the current time-step). The clause should then print out the height of the bounce, store the height of the bounce in an array bounce_heights(), and increment a counter called bounces (initially set to 0). Finally, after the while...endwhile has finished the program should print out the ratios of each bounce height to the previous one. In this way it should be possible to determine the relationship between successive bounce heights and the coefficient of restitution, esp. if you try different values of coeff.

The else clause of an if...then...else statement can also contain several instructions, as the following segment of code demonstrates: day=’sunday’ if day eq ’saturday’ or day eq ’sunday’ then begin print,’It is the weekend’ fun=100 endif else begin print,’It is a week-day’ fun=0 endelse

§6.3. case...of...endcase Sometimes there are a set of definite options that you wish to choose between. In this case the case structure is useful: caterpillar.pro pro caterpillar,day print,’on ’,day,’ the very hungry caterpillar ate:’

Steve Milan, September 2005 33 IDL for IDLers

case day of ’monday’: print,’one apple’ ’tuesday’: print,’two pears’ ’wednesday’: print,’three plums’ ’thursday’: print,’four strawberries’ ’friday’: print,’five oranges’ ’saturday’: begin print,’one piece of cake, one ice cream cone,’ print,’one pickle, one slice of swiss cheese,’ print,’one lollipop, one slice of cherry pie,’ print,’one sausage, one cupcake and one slice’ print,’of watermelon; that night he had a’ print,’stomach ache!’ end ’sunday’: print,’one nice green leaf; he felt much better’ else: print,’that is not a day of the week’ endcase end

IDL> caterpillar,’wednesday’ on wednesday the very hungry caterpillar ate: three plums IDL> caterpillar,’gherkin’ on gherkin the very hungry caterpillar ate: that is not a day of the week

The contents of the variable day are tested against the seven days of the week and the appropriate piece of code run. If multiple statements belong to one clause then they are enclosed in begin...end. If the contents of day do not match any of the op- tions then the code following else: is executed; the else is optional.

§6.3. Algorithms Algorithm is a generic term for a set of rules (the recipe) by which a particular prob- lem can be solved. An algorithm for making sticky gloop might be:

1. start with an empty bowl 2. add flour; go to step 4 3. add water 4. is mixture sticky and gloopy? yes, go to step 7 5. is mixture too dry or sticky? yes, go to step 3 6. is mixture too gloopy? yes, go to step 2 7. finish

As in this example, decision-making often forms an integral part of an algorithm. When writing a computer program to achieve a specific task, the main effort usually involves designing the algorithm(s) that will allow the program to convert the input to the desired output; the (expressing the algorithm in whichever computer lan- guage is being used) is usually straight-forward once the rules have been clearly de- fined. Designing algorithms gets easier with experience; in fact, rarely write programs from scratch, but gain “inspiration” by modifying code that already exists and which solves a problem similar to the one at hand. Hence, interpreting and understanding another programmer’s algorithm is a necessary (and time-saving) skill, which also comes with experience. The following piece of code identifies data gaps (signified by values of -1) in a held in the array x, and linearly interpolates over them. The function interp() should be familiar from task 4.4.

Steve Milan, September 2005 34 IDL for IDLers interpolate_gaps.pro function interp,x0,x1,n ; interpolate between values of x0 and x1 over n points fill=x0+(x1-x0)*findgen(n)/(n-1) return,fill end

;------pro interpolate_gaps x=[0,1,2,1,3,4,-1,-1,-1,-1,-1,7,6,8,9,-1,-1,6,2] plot,x gap_start=-1 for i=0,n_elements(x)-1 do begin if x(i) eq -1 and gap_start eq -1 then gap_start=i-1 if x(i) ne -1 and gap_start ne -1 then begin gap_end=i x(gap_start:gap_end)= $ interp(x(gap_start),x(gap_end),gap_end-gap_start+1) gap_start=-1 endif endfor oplot,x,linestyle=1 end

Task 6.2 Analyse interpolate_gaps.pro and write a step-by-step description of how the algorithm works. It will probably help to enter the program and execute it.

Hint: Often, understanding a program becomes easier if you add lines at strategic points in the code that print out the values of the important variables at each step of the algorithm, so you can keep track of how they change as the algorithm processes the data.

Summary This section has introduced: the logic operators eq, gt, ge, lt, le, ne, and, or, and not the if...then begin...endif construction the case...of...endcase construction the & symbol for placing multiple commands on a single line

Steve Milan, September 2005 35 IDL for IDLers

§7. Input and output

So far we have seen how programs can take input in the form of arguments passed to procedures or functions. Programs can also request specific input from the user or can read information from files or write the output of calculations to files. This chap- ter deals with such input/output (or I/O) issues.

§7.1. The read statement As well as passing arguments to procedures and functions, programs can also request specific input from the user with the read statement:

tables.pro pro times_table,table print,table,’ times table:’ for i=0,9 do print,table,’ x ’,i,’ = ’,table*i end

;------

pro request_table read,’Which times table? ’,table times_table,table end

IDL> times_table,9 9 times table: 9 x 0 = 0 9 x 1 = 9 9 x 2 = 18 ...etc.

IDL> request_table Which times table? 8 8 times table: 8 x 0 = 0 8 x 1 = 8 8 x 2 = 16 ...etc.

In this example the times_table procedure can be called with an argument ta- ble, which specifies which times table to print. Alternatively, request_table can be called without an argument. The read statement prints the (optional) prompt and waits for the user to input a number which is then stored in the variable table. In the next line this is then passed to times_table.

Several variables can be input in the same command:

pythagorean_triplet.pro pro pythagorean_triplet

read,’Lengths of sides of triangle? ’,a,b,c

; sort sides into order of length – the longest, sides(2), is the hypotenuse sides=[a,b,c] sides=sides(sort(sides))

if sides(2)^2 eq sides(0)^2+sides(1)^2 then print,’Pythagorean triplet’

end

Steve Milan, September 2005 36 IDL for IDLers

Aside: Pythagorus’ theorem states that a triangle with sides a, b, and c is right-angled (its sides form a Pythagoean triplet) if a2 = b2 + c2, where a is the hypotenuse. When the three sides are input into the program above it is not clear to the program which is the hypotenuse. The easiest way to determine this is to sort the sides into order of increasing length; the longest is then the hypotenuse. To do this, the array sides is assigned the three lengths [a,b,c], and the sort() function used to sort the array. sort(sides) returns the indices of the array sides in order of the size of the elements of sides, so if sides equals [3,5,4], then sort(sides) re- turns [0,2,1]. sides=sides(sort(sides)) then sets sides equal to [3,4,5]. sides(2) is then the hypotenuse and sides(0) and sides(1) are shorter.

Note that the read command automatically assumes that floating point numbers will be input, and assigns (in the above example) floating point types to a, b, and c. If a string or integer is to be input, then the read statement needs to be primed to expect this:

IDL> name=’’ IDL> read,’Enter your name: ’,name Enter your name: Leonard Nimoy IDL> print,’Your name contains ’,strlen(name),’ characters’ Your name contains 13 characters

Without the initial name=’’, setting name to a string variable (albeit an empty string), the read command would fail when a name was entered with the following error report:

% READ: Input conversion error. Unit: 0, File: % Execution halted at: $MAIN$

This is because it would be unable to interpret the characters as a number, which it would expect if name had not been previously defined. We first encountered this sort of problem in §4.1.

§7.2. Reading from and writing to files: readf and printf The ability to read input becomes most useful when we can read information from files. First we will read strings from a file, then in the next section numbers.

§7.2.1. Reading strings The file RSPP_staff.dat contains the names of members of staff from the Radio and Space Plasma Physics Group (I have added the .dat extension to indicate that the file contains data, not a program):

RSPP_staff.dat Prof SWH Cowley Prof TB Jones Prof M Lester Prof TR Robinson Prof TK Yeoman Dr NF Arnold Dr SE Milan Dr TS Stallard Dr DM Wright Mr J Thornhill

The list is organised from Profs to lowly Drs. We may wish to print the list out in al- phabetical order by surname. The following program reads the contents of the file and prints in alphabetical order:

Steve Milan, September 2005 37 IDL for IDLers

sort_staff.pro pro sort_staff

openr,unit,’RSPP_staff.dat’,/get_lun names=strarr(10) for i=0,9 do begin read_name=’’ readf,unit,read_name names(i)=read_name endfor free_lun,unit

sorted=sort(names) for i=0,9 do print,names(sorted(i))

end

The section of code that is of most interest here is found between the openr and free_lun statements. The openr statements opens a file to read, that is it tells the program that input is going to come not from the keyboard, but from the contents of a file named RSPP_staff.dat. A program can have several files open at any one time (though this program has only one), and to keep track of them IDL assigns a number to each one, and these numbers go by the unwieldy title of logical unit num- bers (LUN); units are anything a computer talks to, including disks, printers, you, or the screen. The number associated with each file is usually held in a variable, in this case the variable unit. We are at liberty to decide on the number ourselves (within certain constraints), but it is easier to let IDL do this automatically, and this is what the /get_lun flag instructs IDL to do. In summary, the openr statement opens the RSPP_staff.dat for reading, and automatically assigns a logical unit number to it, storing it in the variable unit. If we were to subsequently print the value of unit we would probably find that it contained the number 100. If one or more other files were already open, the LUN might be 101 or 102, etc. That is, each open file has a unique identifying number associated with it. There are a limited number of LUNs that can be assigned at any one time, which places a limit on the number of files that can be open simultaneously.

Aside: In the program sort_staff.pro I specified the name of the data file as ’RSPP_staff.dat’. This assumes that the file is located in the directory where IDL expects to find it (in the case of UNIX this would be the directory from which IDL was started). If the file is located in a different directory, then a longer path name could be specified, for example ’../data/RSPP_staff.dat’ or ’/home/tom/data/RSPP_staff.dat’ (in UNIX), or ’C:\Documents and Settings\ets\My Documents\data\RSPP_staff.dat’ (in the case of Windows).

Once these introductory formalities are over, reading the contents of the file is straightforward. In this example, we know that the file contains 10 names on 10 sepa- rate lines, so a string array names with 10 elements is assigned. A for...endfor loop cycles 10 times to read the names. The readf (read formatted) command actu- ally does the reading: this takes the syntax readf,lun,variables where the LUN of the file we wish to read from is held in the variable unit (in this way IDL knows which file to read from if we have several open simultaneously), and as with read one or more variables may be specified. Each call of readf reads one line from the file specified by unit and places the contents in the variable read_name. The names are strings, not numbers, so read_name must be of string type, and it is initiated as such in the line read_name=’’. The last line within the loop stores the

Steve Milan, September 2005 38 IDL for IDLers

name just read into the array names, into the element specified by the loop counter i. Finally, once all the names have been read from the file it is closed by the free_lun statement, which indicates that the LUN held in unit is no longer needed. It is im- portant to close files properly, otherwise a) IDL eventually runs out of available LUNs, b) when writing to files (see below) the output may not be correctly stored if the file is not closed when writing is complete, c) the order of the universe is upset.

The last lines use the sort() function to determine the alphabetical order of the names: sorted becomes an array holding the indices of names in order. These are then printed in order.

This program has two major short-comings. The first is that the program assumes in advance that the file to be read contains 10 lines; the program should be able to cope if it contains only 1 or as many as 100 names. For instance, the contents of the file may change with time (I may get sacked). If fewer than 10 lines existed in RSPP_staff.dat, the program would fail with the following error report:

% READF: End of file encountered. Unit: 100, File: RSPP_staff.dat % Execution halted at: READ_STAFF 7

indicating that the program tried to read a line from the file when no more lines were left. The program should try to determine when the end of the file has been reached, and stop reading at that point.

The second short-coming is that by sorting alphabetically on the whole name, includ- ing title, the names just get arranged so Drs come first and Profs second (an improve- ment at least). To sort properly, the names must be split into title, initials and sur- name, the names then being sorted by surname. The following program overcomes these short-fallings.

sort_staff_II.pro pro sort_staff_II

openr,unit,’RSPP_staff.dat’,/get_lun names=strarr(100) no_names=0 while not eof(unit) do begin read_name=’’ readf,unit,read_name names(no_names)=read_name no_names=no_names+1 endwhile free_lun,unit

print,’Number of staff: ’,no_names

title=strarr(no_names) initials=strarr(no_names) surname=strarr(no_names)

for i=0,no_names-1 do begin name_bits=str_sep(names(i),’ ’) title(i)=name_bits(0) initials(i)=name_bits(1) surname(i)=name_bits(2) endfor

sorted=sort(surname) for i=0,no_names-1 do print,title(sorted(i))+’ ’+surname(sorted(i))

Steve Milan, September 2005 39 IDL for IDLers

end

The program can now read a file of varying length. The file is opened as before. The array names is initialized so that it holds enough elements for 100 staff members. A counter no_names keeps a record of the number of names that have been read in; this is set initially to 0. As the number of lines in the file is not known in advance, a while...endwhile loop is used instead of a for...endfor loop. At each cy- cle of the loop the while not eof(unit) do statement tests the result of the function eof(unit) which is true if the file associated with the lun unit has reached its end of file. If not then the next line is read from the file, this is placed in names(no_names), and no_names is incremented by 1. When the eof is reached the while loop ends and the file is closed as before. Now the variable no_names holds a count of the number of names that were read into the array names, such that the elements names(0:no_names-1) hold the names and names(no_names: 99) will contain empty strings. In this way, the program will read a file containing an arbitrary number of lines, and would work, for example, if RSPP_staff.dat was replaced by a file containing the names of all members of the Physics department; the program is flexible.

The last section of code separates the names into title, initials and surname, and sorts alphabetically on the surname only. It is left as an exercise for the reader to under- stand how this works.

So far we have dealt with reading in a file, but we may also want to create files con- taining output. In the above program we may want to create a file containing the names in alphabetical order. This is simply achieved with the openw (open to write) and printf (print formatted) statements, whose syntax is identical to openr and readf.

Task 7.1 Add lines to the end of sort_staff_II.pro which write the sorted list of staff to an output file entitled RSPP_staff_sorted.dat. This should involve an openw statement, a for...do loop which printfs the names in alphabetical order, and a free_lun statement. Check that the output file contains what you expect by nediting it.

§7.2.2. Reading numbers We can also read numbers from files; the following file contains a list of (x, y) points for drawing a house:

house.dat -1.0 1.0 1.0 1.0 1.0 -1.0 -1.0 -1.0 -1.0 1.0 0.0 2.0 1.0 1.0

and the following reads these coordinates and plots them:

plot_house.pro

Steve Milan, September 2005 40 IDL for IDLers

pro plot_house openr,unit,’house.dat’,/get_lun x=fltarr(20) & y=fltarr(20) x_in=0.0 & y_in=0.0 & pts=0 while not eof(unit) do begin readf,unit,x_in,y_in print,x_in,y_in x(pts)=x_in & y(pts)=y_in pts=pts+1 endwhile free_lun,unit window,0,xsize=500,ysize=500 plot,x(0:pts-1),y(0:pts-1),xrange=[-3,3],/xstyle,yrange=[-3,3],/ystyle, $ pos=[0.1,0.1,0.9,0.9] end

Each line of the input file contains two numbers, and the readf statement in the pro- gram asks for two input variables x_in and y_in (previously assigned as floating point). In this case IDL looks for two numbers on each input line, separated by an arbitrary number of blank spaces, and assigns them to the two input variables. This technique can be used for any number of columns. If the are more input variables specified in the readf statement than IDL can find numbers on an individual line of the input file then it will read the next line looking for the extra values. Alternatively, if there are more numbers than input variables, the extra ones are ignored.

§7.2.3. The format statement There are situations where the input file is not as neat and tidy as above (simple space-delineated columns). Below is an small excerpt from the file so- lar_wind.dat which lists hourly values of solar wind proton number density and solar wind bulk speed for January 2005 measured by the Advanced Composition Ex- plorer (ACE) spacecraft located at the L1 point upstream of the Earth (the full file can be viewed by nediting it). ACE provides advanced-warning of the solar wind con- ditions that will impinge on the Earth as it takes ~1 hour for the solar wind to propa- gate from L1 to the nose of the magnetosphere.

Columns: 0123456789012345678901234567890123456789012345678901234567890123456789 ... EPOCH H_DENSITY SW_H_SPEED dd-mm-yyyy hh:mm:ss.ms #/cc km/s

01-01-2005 00:00:00.000 5.91690 429.590 01-01-2005 01:00:00.000 6.49870 432.990 01-01-2005 02:00:00.000 6.35480 436.990 01-01-2005 03:00:00.000 6.59420 428.070 01-01-2005 04:00:00.000 7.24800 442.900 01-01-2005 05:00:00.000 6.88640 437.330 01-01-2005 06:00:00.000 7.15350 436.290 01-01-2005 07:00:00.000 7.38090 424.900 ... (Data taken from CDAWeb: http://cdaweb.gsfc.nasa.gov/cdaweb/istp_public/)

The file consists of 52 lines of descriptive text (usually known as a header), followed by (approximately) 700 lines of data (24 hourly values for the 31 days of January). To read the contents of the file will take two stages: a) skip the header, b) read the da-

Steve Milan, September 2005 41 IDL for IDLers

ta from lines that are not simply space-delineated. The first part is straight-forward: first the file is opened and then 52 lines of text are read:

... openr,unit,’solar_wind.dat’,/get_lun header=’’ for i=0,51 do readf,unit,header ...

In the lines of data there is date and time information, including year, month, day, and hour, minute, and seconds, followed by the density and speed. As the data has only hourly values, the only date/time information we are interested in are the day of the month and the hour of the day (from which we can calculate the hour from the start of the month). We need to tell IDL how to pick out the relevant data from the morass of extraneous information, and to do this we must specify the format of the data in the readf statement:

... month_hours=fltarr(31*24) & Vsw=fltarr(31*24) & Nsw=fltarr(31*24) day=0 & hour=0 & Vsw_in=0.0 & Nsw_in=0.0 & pts=0 while not eof(unit) do begin readf,unit,day,hour,Nsw_in,Vsw_in,format=’(i2,9x,i2,10x,f14.5,f14.3)’ month_hours(pts)=(day-1)*24+hour Vsw(pts)=Vsw_in Nsw(pts)=Nsw_in pts=pts+1 endwhile free_lun,unit ...

The format statement is a string that starts and ends with parentheses (). Inside of these is a comma-delineated list of the expected contents of the line. In the present example these are (compare this with the extract from solar_wind.dat above; the top line of 0123456789... is intended to help you count the columns):

i2 a 2-digit integer (the day of the month) 9x 9 characters to be ignored i2 a 2-digit integer (the hour of the day) 10x 10 characters to be ignored f14.5 a floating point number that is 14 characters wide (maybe blank spaces first), with 5 digits following the decimal point (the solar wind density) f14.3 a floating point number that is 14 characters wide (maybe blank spaces first), with 3 digits following the decimal point (the solar wind speed)

Note that within the data file the value -1.00000E+31 (i.e. a very large negative number) is used as a bad data flag for Nsw or Vsw. This does not fit the expected format (e.g. f14.5), but IDL tries to cope gracefully with these situations.

Another useful format code is an which represents n alphanumeric characters (i.e. a string). Formats can be specified not just for reading in information, but also printing it out with printf or print statements. For example, compare the following un- formatted and formatted output:

IDL> height=[1.67,1.75,1.68,1.8,1.85] IDL> num=n_elements(height) & mean_h=total(height)/n_elements(height) IDL> print,num,’ people of mean height ’,mean_h,’ m’ 5 people of mean height 1.75000 m IDL> print,num,’ people of mean height ’,mean_h,’ m’,format=’(i1,a,f4.2,a)’ 5 people of mean height 1.75 m

Steve Milan, September 2005 42 IDL for IDLers

Aside: note the use of total() which gives the sum of the elements of the array specified.

If a format is not specified, IDL uses a default format for integers and floating point numbers: i12 and f13.5, respectively. Sometimes this doesn’t lead to “pretty” out- put, and specifying a format can tidy things up. Not only this, but it can be important to specify a format when writing to an output file, so that the same format statement can be used for reading it in again. Note that in the example above the a format code does not have an associated number so it just prints the required number of characters.

Task 7.2 Use the example code above to write a procedure to read the contents of so- lar_wind.dat and plot the speed and density for the month of January 2005. Cal- culate the solar wind ram pressure (high pressure leads to compression of the magne- tosphere): 2 Psw = ρ swVsw where ρsw is the mass density of the solar wind, given by mpnsw where mp is the proton mass and nsw is the number density. Remember to convert all values to SI units. Watch out for the bad data flag. The nose of the magnetosphere is compressed into a distance of Rmp from the Earth by the impinging solar wind, where 1  B 2  6 R = R  eq  , mp E  µ   0 Psw  determined by pressure balance between the solar wind and the internal magnetic pressure. Beq is the equatorial magnetic field strength at the surface of the Earth (31,000 nT). If Rmp decreases below 6.6 RE then satellites in geosynchronous orbit near local noon can suddenly find themselves in the solar wind. Calculate Rmp and create a new output file with three columns: time (in hours from the start of the month), Rmp, and some text which is either ‘magnetosphere’ or ‘solar wind’ for Rmp > 6.6 RE or Rmp < 6.6 RE, and ‘no data’ if there is a data gap.

Aside: The solar wind velocity varies between 300 and 1000 km s-1, blown outwards by the high temperature of the solar corona. Typical densities are a few protons cm-3, though there are compression regions which occur

Steve Milan, September 2005 43 IDL for IDLers

where a fast solar wind stream catches up with a slow stream, scooping up the mass in front of it. In rarefaction regions the solar wind pressure is ~1 nPa, though this can increase to many 10s nPa in compression regions. These interaction regions can lead to strong disturbances of the magnetosphere.

§7.2.3. Finding files – the file_search() function So far in this chapter, when opening and reading a file we have known its filename in advance. Sometimes we want to read lots of files (and don’t want to have to enter all the names by hand) or we may not know in advance what they are called. In such cases it is easiest to let IDL determine the names for itself. As an example, I will de- scribe the data set collected by the Far Ultraviolet (FUV) instrument onboard the NASA Imager for Magnetopause-to-Aurora Global Explorer (IMAGE) spacecraft, a copy of which is help on a server in the department.

IMAGE orbited the Earth between May 2000 and October 2005 taking approximately 5 million images of the UV aurora with the three auroral cameras which comprised the FUV instrument, known as the Wideband Imaging Camera (WIC), and the Spec- trographic Imager channels 12 (SI12) and 13 (SI13). Each image is held in an indi- vidual data file with a name of the format yyyymmddhhmmss_c.dat, that is a 4- digit year and 2-digit month, day, hour, minute, and second corresponding to the time at which the image was taken; the c corresponds to WIC, S12 or S13 depending on the camera. In other words, the time information is stored in the file name rather than in the file itself; if the files were called image_1.dat, image_2.dat, etc., it would be very difficult to find the files corresponding to the time you were interested in! Even with this sensible naming convention, it is impractical to hold all the files in a single directory, so the files are stored in a directory structure: there is a top level directory called /data/image_fuv within which are subdirectories 2000, 2001, ... 2005 corresponding to each year of the mission; within each of these there are subdirectories 01, 02, ... 12 corresponding to the months of the year, and each of these then holds the relevant files. Hence, the full path name of a WIC image collect- ed shortly after midnight on the 1st November 2001 might be: /data/image_fuv/2001/11/20011101001356_wic.dat.

The user cannot possibly type in all 5 million file names to read in all the data. In- stead, the function file_search() is used. For instance, if we want to find all the images collected by WIC between the hours of 12 and 13 on 1st November 2001 we could use (don’t try this at home: these files don’t exist on your computer!)

IDL> files=file_search(’/data/image_fuv/2001/11/2001110112*_wic.dat’, COUNT=nfiles) IDL> print,nfiles 27 IDL> print,files /data/image_fuv/2001/11/20011101120110_wic.dat /data/image_fuv/2001/11/20011101120313_wic.dat /data/image_fuv/2001/11/20011101120516_wic.dat ...

indicating that there are 27 such files, and these files correspond to images taken at 12:01:10, 12:03:13, 12:05:16 UT, etc. The file_search() function seeks files that match the specified search string, where * is a wild card, returning a string array holding the list of file names. The second argument to file_search(), in this case nfiles, names a variable that should be created by the function and which will hold the number of files that match the search string. Hence, in this example the vari-

Steve Milan, September 2005 44 IDL for IDLers

able files will be assigned a string array with 27 elements. If no files are found, nfiles will be set to the value 0 and files will be set to an empty string.

The file_search() function could be incorporated into a program to analyse all the required files in turn:

... files=file_search(’/data/image_fuv/2001/11/2001110112*_wic.dat’,nfiles) if nfiles gt 0 then begin for i=0,nfiles-1 do begin openr,unit,files(i),/get_lun ... free_lun,unit endfor endif ...

Note that the program checks that some files were found that matched the search string, i.e. nfiles is nonzero, before trying to open them.

This is not the end of the story. If the user wants to find data files from a different year or month then the path name will change. However, because the path name has a predictable structure, IDL can be made to construct it. Say the date required is speci- fied in the variables yr, mo, and dy. The path name is just a string, so can be con- catenated from its constituent parts as we saw in §4.1:

... yr=0 & mo=0 & dy=0 read,’Enter year, month, day: ’,yr,mo,dy pathname=’/data/image_fuv/’+string(yr,fo=’(i04)’)+’/’+string(mo,fo=’(i02)’) search=string(yr,mo,dy,fo=’(i04,i02,i02)’)+’*_wic.dat’ print,’Searching directory: ’+pathname print,’for files matching search string: ’+search files=file_search(pathname+’/’+search,nfiles) print,’Found: ’,nfiles print,files ...

An example of the output might be:

Enter year, month, day: 2002,10,13 Searching directory: /data/image_fuv/2002/10 for files matching search string: 20021013*_wic.dat Found: 541 /data/image_fuv/2002/10/20021013000013_wic.dat /data/image_fuv/2002/10/20021013000216_wic.dat ...

Here, the function string() produces an output string made up of the variables supplied to it, with the format specified (notice that format has been abbrev. to fo; see §3.3). The format code i02 indicates that the output integer should contain two digits, padded with leading zeros if it is less than 10: hence, for yr, mo, and dy with values 2003, 2, and 1,

string(yr,mo,dy,fo=’(i4,i2,i2)’)

would give the string ’2003 2 1’ but

string(yr,mo,dy,fo=’(i04,i02,i02)’)

Steve Milan, September 2005 45 IDL for IDLers gives ’20030201’, as required.

The directory Mountsorrel contains air temperature measurements from a weather station located 4 miles north of Leicester (http://www.stormtrack.co.uk/) for the year 2008. It contains the subdirectories 2008_Jan, 2008_Feb, etc. Within each sub- directory are data files containing the temperature measurements for each day of the month (though some days may be missing if the weather station was inoperative).

Task 7.3 Write a program that uses file_search() to print out the names of all the data files contained in the subdirectories of Mountsorrel.

Hint: the subdirectory names each contain three letters corresponding to the name of each month; these could be held in an array, e.g. months=[’Jan’,’Feb’,...]. Use a for...endfor loop to cycle through the sub- directories.

Each data file contains 24 values corresponding to the air temperature recorded on the hour each hour of the day.

Task 7.4 Modify your program from the previous task to determine the average daily tempera- ture variation at Mountsorrel for each month of 2008.

Hint: the mean variation through several arrays can be found by adding the arrays and dividing by their number, e.g.

IDL> print,([1,2,3,4,5]+[1,2,4,4,4]+[2,2,3,5,6])/3.0 1.33333 2.00000 3.33333 4.33333 5.00000

A practical solution to determining the mean variation from many arrays (each in this case containing 10 elements) is outlined as follows:

... mean_array=fltarr(10) for i=0,n-1 do begin array=fltarr(10) ... ; determine values of nth array, e.g. read contents of file mean_array=mean_array+array/n endfor print,mean_array ...

Summary This section has introduced: the read statement the openr, openw, and free_lun statements logical unit numbers readf and printf format descriptors the file_search() function the sort()and total() functions finding the average of several time series

Steve Milan, September 2005 46 IDL for IDLers

§8. Plotting

A picture is worth a thousand words, and graphical output of data is much easier to interpret than columns of numbers. This chapter deals with the basics of plotting data in IDL.

§8.1. Creating a plot window As we have seen previously, executing a plot,x,y command will automatically open a plotting window, though this will be of the default size of 640×512 pixels, which may not be appropriate for your output. The window command opens a win- dow of any specified size, e.g.:

IDL> window,0,xsize=500,ysize=500,retain=2

Several plot windows can be open simultaneously, and the first number specifies that this is to be number 0 (it could be any number), and the window will be 500×500 pix- els in size. xsize and ysize are optional, but if they are not specified the default size will be used. The retain=2 is also optional, but has the useful property that the window’s contents are redrawn if at any point the window becomes covered (trust me, it is useful). If a window already exists but contains a plot that you no longer re- quire, use the erase command to clear it.

§8.2. The plot and oplot commands We have met the plot command several times before, but have not investigated it in all its gory details. In its simplest form two arrays containing x and y coordinates of the points to be plotted in a graph are specified:

IDL> half_life=600 IDL> n0=800 IDL> t=findgen(60)*100 IDL> n=n0*2^(-t/half_life) IDL> plot,t,n,xtitle=’t (s)’,ytitle=’n’,title=’Radioactive decay’

Here, xtitle and ytitle define the labels for the x- and y-axes and title gives the plot a title; these are optional. IDL automatically scales the axes to fit the values in t and n. However, it is possible to override this scaling with xrange=[xmin, xmax] and yrange=[ymin,ymax]. One of the idiosyncrasies of IDL is that even if you specify the desired ranges on the axes it doesn’t necessarily believe you, and you have to use xstyle=1 and ystyle=1 to specify that you mean it:

IDL> plot,t,n,xtitle=’t (s)’,ytitle=’n’,title=’Radioactive decay’, xrange=[0,4000],/xstyle

The xrange=[0,4000] sets the scale for the x-axis and the /xstyle means “no, really!”. Other useful keywords for the plot command are linestyle=n which sets the type of line used and psym=n which sets which symbol to plot. The default is linestyle=0 (full line) and psym=0 (no symbol). Linestyle=1 is a dotted line, 2 dashed, etc. Psym=1 is a cross, 3 is a dot, etc. If the value of psym is posi- tive then only symbols are plotted; if psym is made negative then symbols and a line are plotted. For instance, the combination linestyle=3,psym=-1 would plot crosses at the data points, joined by a dot-dashed line.

Steve Milan, September 2005 47 IDL for IDLers

Task 8.1 Psym can take values between 0 and 7. Determine which symbols are associated with each value.

Subsequent lines can be over-plotted on already existing axes with the oplot command. This has the same syntax as plot, except that keywords which affect the axes (which already exist), such as xtitle, xrange, etc., cannot be used. For instance, having already plotted the radioactivity decay graph above, the decay of a second substance could be overlaid:

IDL> half_life=1500 IDL> n0=500 IDL> n=n0*2^(-t/half_life) IDL> oplot,t,n,linestyle=2

Another method to plot several curves in the same win- dow is to have multiple sets of axes. By default IDL places axes approximately centred in the window, but the position keyword allows alternative placement of ax- es such that several can be plotted. The bottom left-hand and top right-hand corners of the window have coordi- nates (0,0) and (1,1), respectively. The position keyword specifies a four element array defining the bot- tom-left (x0,y0) and top-right (x1,y1) coordinates of the desired axes: position=[x0,y0,x1,y1]. For in- stance, the following code plots two axes in the same window:

IDL> window,0,xsize=500,ysize=700 IDL> theta=2*!pi*findgen(101)/100-!pi IDL> plot,theta,cos(theta),xrange=[-!pi,!pi],/xstyle,position=[0.15,0.55, 0.85,0.85] IDL> plot,theta,cos(theta*2),xrange=[-!pi,!pi],/xstyle,position=[0.15,0.15, 0.85,0.45],/noerase

Two additional keywords are useful: min=n and max=n specify the minimum and maximum values that the plot command will plot. For instance if the value 10000 represents a bad data flag then max=9999 will avoid plotting the bad data. Try add- ing min=-0.5 to the previous two plot commands.

The default behaviour of plot is to erase the contents of the window prior to drawing a graph, so when the second plot command is executed it is necessary to specify /noerase to tell it not to erase the first graph. The size of the text used to label the axes can be controlled with the charsize keyword; the default is charsize=1. Hence, charsize=1.5 would use labelling 1.5 times the normal size.

Steve Milan, September 2005 48 IDL for IDLers

Task 8.2 Fourier theory states that any curve can be decomposed into an infinite series of sines and cosines. For instance, a square wave is composed of an infinite series of sines: sin kθ S(θ ) = ∑ k =1,3,5,... k Write a program to plot the first five components of this series, in the range 0 < θ < 4π, and the sum of these, as indicated left.

Lastly, a technique we came across in bouncing_ball.pro (§6.2) is to create some empty axes and then fill in the graph afterwards with oplot. For instance,

IDL> plot,[0],[0],/nodata,xrange=[0,10],yrange=[-10,10] IDL> for x=0.0,10.0,0.01 do oplot,[x],[x*sin(x*5)],psym=3

The plot command creates the axes, with the /nodata keyword specifying that no curve is to be drawn; you still need to specify x and y arrays, but these data are not plotted: the simplest is [0],[0], two 1-element arrays containing 0. Thereafter, points (psym=3) are plotted at the (x,y) position specified in the oplot command. Although each call of oplot plots just one point (x, x sin 5x), oplot still expects the x and y information to be supplied as arrays; the [x],[x*sin(x*5)] makes both a 1-element array.

There are a whole host of other plotting options for plot: a full list of keywords can be found in the reference manual. The next section deals with the color keyword.

§8.3. Colour Different colours can be used in plotting commands, defined using the keyword col- or in the plot or oplot command:

IDL> plot,[0],[0],/nodata,xrange=[-10,10],yrange=[-10,10] IDL> angles=2*!pi*findgen(101)/100 IDL> for i=1,10 do oplot,i*sin(angles),i*sin(angles),color=i*10

This plots 10 concentric circles of different colours (specifically, colours with num- bers 10, 20, 30, ...). The actual colours that are plotted depends on the colour table that has been loaded or defined. Colour 0 usually defines the background colour (black), and the default colour table has 1 as the foreground colour (white). Plot defaults to plotting things in colour 1. The easiest way to change the colour table is through use of the

IDL> xloadct

(X load colour table) command (try it). A range of colour tables can be selected from. These usually define colour tables that contain 256 colours (i.e. color can take val- ues between 0 and 255). Loadct,n also fulfils the same function, but the number of the colour table (0-40) is specified; if the n is not supplied then the available list is printed. Finally, you can create your own colour table:

IDL> tvlct,[0,255,255,0,0],[0,255,0,255,0],[0,255,0,0,255]

Steve Milan, September 2005 49 IDL for IDLers

defines a 5-colour table. The three arrays specify the red, green, and blue (RGB) components of each colour, so colour 0 has RGB components (0,0,0), or black, colour 1 is (255,255,255) or white, colour 2 is (255,0,0) or pure red; colours 3 and 4 are simi- larly defined as pure green and pure blue. The RGB values can span 0 to 255, so (100,100,100) would define grey.

Aside: depending on the computer system you are using you may only get shades of grey or red. One of the fol- lowing may cure this. Exit IDL and restart it. On start-up immediately type IDL> device,pseudo=8 or IDL> device,decompose=0

§8.4. tv and tvscl An alternative method of producing graphical output is to build up the picture pixel by pixel. The pixel information is held in a 2-dimensional array, and could be the result of a calculation or read in from a file. In the following examples we will use a relief (altitude) map of Britain, the data being held in the file map.hgt, which can read as follows:

IDL> openr,unit,’map.hgt’,/get_lun IDL> alt=intarr(900,900) IDL> readu,unit,alt IDL> free_lun,unit

The map covers the longitude range 7°W (-7°E) to 2°E and 50°N to 59°N in 900×900 pixels, representing a spatial resolution of ~1 km; altitudes are measured to the near- est metre above a reference spheroid (effectively height above sea level). The map can be output very straightforwardly:

IDL> loadct,13 IDL> tv,alt

First a colour table is selected (in this case a standard blue-to-red spectrum). Then tv takes the elements of the array alt and places them pixel by pixel in the plot win- dow, where each pixel has the colour corresponding to each value in alt.

Aside: these data were originally downloaded from ftp://e0mss21u.ecs.nasa.gov/srtm/ which contains SRTM (Shuttle Radar Terrain Mission) altitude data for ~80% of the Earth’s landmass, derived from a synthetic aperture radar (SAR) experiment run on a Space Shuttle mission in February 2000. The original data came in 1° longitude × 1° latitude tiles with ~100×100 m resolution. These tiles were stitched together and rescaled to the ~1 km reso- lution found in map.hgt.

There are two main problems here. The array has 900×900 elements, but the plot window is probably smaller than 900×900 pixels, so only the southwestern corner of the map is seen. This can be rectified by reducing the size of the array and defining an appropriately sized window:

IDL> alt=rebin(alt,450,450) IDL> window,0,xsize=450,ysize=450 IDL> tv,alt

The rebin() function averages together elements or interpolates between them to make an array of a different size, in this case 450×450. Also, there are only 256 col- ours in the colour table, but the maximum value in alt exceeds this (indeed max(alt) indicates that the maximum value is ~1200). The colour plotted at each point is then alt mod 256, and colours cycle repeatedly through the colour table as the values in alt increase. To overcome this, apply appropriate scaling (e.g. di-

Steve Milan, September 2005 50 IDL for IDLers vide the values in alt by 5 such that they fall between 0 and 256), or use tvscl,alt which automatically applies a scaling factor.

§8.5. contour and surface The tv command is useful for plotting when we have a 2-dimensional array of the right size to plot pixel by pixel in a window. Often, however, we want to plot a spars- er data set, one that contains, say, 30×30 data points. In this situation contour or surface are useful. For instance, the following extracts a small portion from the alt array (in the vicinity of Leicester) and produces a contour plot of the terrain. contour_leicester.pro pro contour_leicester alt=intarr(900,900) openr,unit,'map.hgt',/get_lun readu,unit,alt free_lun,unit

; longitudes from 7W to 2E, latitudes from 50N to 59N lon=indgen(900)/100.-7 ; find longitude (E) of each element across array lat=indgen(900)/100.+50 ; find latitude (N) of each element up array

; find indices of array elements that correspond to +/- 0.125 degrees of ; lon and lat of the clock tower in Leicester town-centre leic_pos=[-1.13,52.64] lon_ind=where(lon ge leic_pos(0)-0.125 and lon le leic_pos(0)+0.125) lat_ind=where(lat ge leic_pos(1)-0.125 and lat le leic_pos(1)+0.125)

; create sub-arrays of map information for the Leicester area leic_alt=alt(min(lon_ind):max(lon_ind),min(lat_ind):max(lat_ind)) leic_lon=lon(lon_ind) leic_lat=lat(lat_ind) loadct,13 window,0,xsize=500,ysize=500 contour,leic_alt,leic_lon,leic_lat,xrange=leic_pos(0)+[-0.125,0.125], $ /xstyle,yrange=leic_pos(1)+[-0.125,0.125],/ystyle, $ xtitle=’longitude’,ytitle=’latitude’, $ levels=indgen(25)*5+50,/fill,c_col=indgen(25)*10 contour,leic_alt,leic_lon,leic_lat,levels=indgen(15)*10+50,/overplot,/follow end

Most of the code in contour_ leicester.pro reads in the data and extracts a sub-array containing height in- formation into a 25×25 element array (the number of grid points within ±0.125° of latitude and longitude of the clock tower, along with the corresponding latitudes and longitudes of the grid points. All the hard work is then done in the two con- tour commands. The main constituents of a contour command are:

IDL> contour,z,x,y

Steve Milan, September 2005 51 IDL for IDLers

where z is a 2D array holding, say, height information, and x and y are two 1D arrays holding the (x,y) locations of each point in z.

If x and y are omitted it is assumed that the x and y positions are 0, 1, 2, 3,... In the present case z is leic_alt and x and y are leic_lon and leic_lat. Every- thing else in the command are keywords to modify the behaviour of the contouring. The xrange, and xstyle keywords we have already met in the context of plot. The first contour command produces a filled (/fill) contour plot, with 25 con- tour levels (50, 55, 60, 65, ...) expressed in contour colours (c_cols) 0, 25, 50, 75, ... The second contour command superimposes (/overplot) contour lines (no /fill keyword) at 15 levels 50, 100, 150, ... The /follow keyword indicates that the contours should be labelled (go figure).

Task 8.3 Read in the whole altitude information from map.hgt as before. Rebin the data to a 100×100 grid. Then contour at altitudes of 0.1 m and 10 m, indicating how the coastline of Britain would change if sea-levels rose markedly from the present level.

The surface and shade_surf commands are similar to contour, but produce 3D wire-frame and shaded surfaces, respectively. For instance, the two figures below could be produce by exchanging the contour commands in con- tour_leicester.pro with either of the commands:

surface,leic_lat,leic_lon,leic_lat,zrange=[0,500] shade_surf,leic_lat,leic_lon,leic_lat,zrange=[0,500]

§8.6. Annotation and the xyouts command Annotation can be added to plots. For example, after running con- tour_leicester.pro to produce a contour plot of the Leicester area, the loca- tion of the clock tower could be indicated with the following commands:

IDL> oplot,[-1.13],[52.64],psym=3 IDL> xyouts,-1.13,52.64,’ Clock tower’

The oplot marks the location of the clock tower with a cross, and xyouts (output a string at a specified x-y location) then adds text, again at the same location. As xyouts is usually used to add one line of text, the (x,y) information is not expected to be arrays so the [] are not necessary, unlike oplot. In addition, keywords such as color and charsize can be used to change the appearance of the text. The bot- tom left hand corner of the first character of the string is plotted at location (x,y) and the rest of the string follows this to the right; for this reason I have added a blank space at the start of the string so that the C of Clock does not overwrite the cross marking the location. The align keyword modifies the behavior: align=0 is the de-

Steve Milan, September 2005 52 IDL for IDLers fault (left-justified), align=1 would right-justify the text and align=0.5 would centre it.

Task 8.4 The file landmarks.dat contains latitude, longitude and name infor- mation of other landmarks in the Leices- ter area. Modify con- tour_leicester .pro to read in the landmark information and plot their locations on the map.

§8.7. Output of plots, tvrd() and write_png In §8.4. we found that tv could be used to load the contents of an array into a plot window. The corollary of this is the tvrd() function which instead loads the con- tents of the plot window into an array:

IDL> image=tvrd() creates a 2D array image with the same number of elements as the window has pix- els, the elements containing the colour numbers associated with the picture. IDL has several commands for creating picture files (e.g. .jpeg, .gif) from this information. One of the most portable image formats is .png (portable network ) and IDL has a write_png command; this was used to produce all the images imported into this document. To create the image file correctly, the colour table information must be passed to write_png, and this can be found using tvlct, for example:

IDL> image=tvrd() IDL> tvlct,r,g,b,/get IDL> write_png,’piccy.png’,image,r,g,b

As before, the first line loads the window contents into the array image. The tvlct command, with the /get keyword specified, reads the red, green, and blue infor- mation from the colour table and puts this in arrays r, g, and b. Finally, the image and colour table information is passed to write_png, along with the desired file- name.

Summary This section has introduced: window and erase plot and oplot tv and tvscl colour tables, xloadct, loadct, and tvlct contour, surface, and shade_surf

Steve Milan, September 2005 53 IDL for IDLers

output of image information: tvrd() and write_png rebin() a small selection of the hostelries I have recently frequented in Leicester

Steve Milan, September 2005 54 IDL for IDLers

§9. A worked example - Pogo

This chapter shows how a program might be constructed from first principles. I hope to show here that programs are not written “in one go” but are built up section by sec- tion, testing each section for bugs as the program grows.

The aim of the program is to recreate a simplified version of a computer teaching aid, first developed in the 1970s, called Logo. This was a computer language that allowed school children to draw pictures by providing sequences of instructions. These - tures could be drawn on the computer screen, or in more sophisticated versions a small robot (called a “turtle”) moved a pen around a large sheet of paper placed on the floor. The turtle could be instructed to “move forwards 20 cm”, “turn through 90°”, “lift the pen off the paper”, “put the pen down on the paper”, etc. We will now write a program to recreate this, and call it Pogo.

In the first instance, the program will interpret the following instructions: draw n move forward n steps, drawing a line move n move forward n steps without drawing a line turn n turn through an angle of n° to point in a new direction clear erase the picture end finish

First it will need to open a plotting window, and draw a set of axes, assuming the pa- per extends 200 units in each direction. We assume that the “turtle” starts at an (x, y) position of (0, 0), pointing at an angle of 0° (up the screen). We can start the program as follows:

pogo.pro pro pogo

window,0,xsize=500,ysize=500 plot,[0],[0],/nodata,xrange=[-100,100],/xstyle,yrange=[-100,100],/ystyle

x=0.0 y=0.0 angle=0.0

end

The program is trivially simple at this point, but we can compile and execute it to check that we have introduced no errors so far. If there are any then they will be easy to spot and correct.

The next stage is to ask for input from the user, expecting that this will be of the form defined above, that is either a one-word instruction, or a one-word instruction fol- lowed by an argument. This can be achieved as follows:

pro pogo

window,0,xsize=500,ysize=500 plot,[0],[0],/nodata,xrange=[-100,100],/xstyle,yrange=[-100,100],/ystyle

x=0.0 y=0.0 angle=0.0

input=’’

Steve Milan, September 2005 55 IDL for IDLers

read,’Pogo> ’,input input_words=str_sep(input,’ ’) instruction=input_words(0) if n_elements(input_words) eq 2 then argument=input_words(1) print,instruction print,argument end

The input is expected to come in the form of a string (it has characters as well as numbers), so the variable input is set to an empty string. Then the following read statement knows that a string is expected. The input may come in one or two words, and we can separate these using str_sep(). The instruction is the first word. n_elements() checks whether two words were input, in which case the argu- ment is the second word. Finally, the print statements allow us to check that eve- rything is working as we expect at this stage. Try out the program, and check that two-word input is successfully interpreted as an instruction and an argument. After this we can remove the print statements.

Next, the program will need to differentiate between a variety of instructions, and a case...endcase statement is probably the best way to achieve this. pro pogo window,0,xsize=500,ysize=500 plot,[0],[0],/nodata,xrange=[-100,100],/xstyle,yrange=[-100,100],/ystyle x=0.0 y=0.0 angle=0.0 input=’’ read,’Pogo> ’,input input_words=str_sep(input,’ ’) instruction=input_words(0) if n_elements(input_words) eq 2 then argument=input_words(1) case instruction of

’draw’: print,’the draw code goes here’

’move’: print,’the move code goes here’

’turn’: print,’the turn code goes here’

’clear’: print,’the clear code goes here’

’end’: print,’the end code goes here’

else: endcase end

We can now test this version of the program to check that when we type in instruc- tions, the program selects the right section of code to execute. Notice the else: in the case statement: this hoovers up any input that doesn’t match the expected in- structions and ignores it; without this the program would crash every time an incorrect

Steve Milan, September 2005 56 IDL for IDLers statement was entered. Finally, we implement the pogo instructions one by one. The first is draw n. This command should find a new turtle position, taking into account that it starts at (x, y), is pointing in direction angle, and should move a distance n, which is held in the variable argument. The new position can be found with: new_x=x+float(argument)*sin(angle*!dtor) new_y=y+float(argument)*cos(angle*!dtor)

Note the use of float() to turn argument, which is a string, into a number. Then, to draw a line from the old to the new position we use oplot,[x,new_x],[y,new_y]

Finally, the turtle updates its position (held in x and y) to the new calculated position x=new_x y=new_y

While we are at it, the move n instruction can be implemented; it is exactly the same as draw but we do not plot the line. The program now looks as follows: pro pogo window,0,xsize=500,ysize=500 plot,[0],[0],/nodata,xrange=[-100,100],/xstyle,yrange=[-100,100],/ystyle x=0.0 y=0.0 angle=0.0 input=’’ read,’Pogo> ’,input input_words=str_sep(input,’ ’) instruction=input_words(0) if n_elements(input_words) eq 2 then argument=input_words(1) case instruction of

’draw’: begin new_x=x+float(argument)*sin(angle*!dtor) new_y=y+float(argument)*cos(angle*!dtor) oplot,[x,new_x],[y,new_y] x=new_x y=new_y end

’move’: begin new_x=x+float(argument)*sin(angle*!dtor) new_y=y+float(argument)*cos(angle*!dtor) x=new_x y=new_y end

’turn’: print,’the turn code goes here’

’clear’: print,’the clear code goes here’

’end’: print,’the end code goes here’

else: endcase

Steve Milan, September 2005 57 IDL for IDLers

end

We can then test the program as it stands, before turning to the last instructions: turn n: simply add argument to angle. clear: replot the screen and reset the location of the turtle end: jump out of the program.

We also need an infinite loop, implemented using goto, so that many commands can be entered one after another. To implement the end instruction, we then use the re- turn command to drop out of the procedure. The final version of the code is as fol- lows: pro pogo window,0,xsize=500,ysize=500 plot,[0],[0],/nodata,xrange=[-100,100],/xstyle,yrange=[-100,100],/ystyle x=0.0 y=0.0 angle=0.0 start_of_loop: input=’’ read,’Pogo> ’,input input_words=str_sep(input,’ ’) instruction=input_words(0) if n_elements(input_words) eq 2 then argument=input_words(1) case instruction of

’draw’: begin new_x=x+float(argument)*sin(angle*!dtor) new_y=y+float(argument)*cos(angle*!dtor) oplot,[x,new_x],[y,new_y] x=new_x y=new_y end

’move’: begin new_x=x+float(argument)*sin(angle*!dtor) new_y=y+float(argument)*cos(angle*!dtor) x=new_x y=new_y end

’turn’: angle=angle+float(argument)

’clear’: begin plot,[0],[0],/nodata,xrange=[-100,100],/xstyle, $ yrange=[-100,100],/ystyle x=0.0 y=0.0 angle=0.0 end

’end’: return

else:

Steve Milan, September 2005 58 IDL for IDLers

endcase goto,start_of_loop end

We now have our simple implementation of Pogo. We can draw a square with the following commands:

Pogo> draw 30 Pogo> turn 90 Pogo> draw 30 Pogo> turn 90 Pogo> draw 30 Pogo> turn 90 Pogo> draw 30 Pogo> end

Task 9.1 Use Pogo to draw a pentagram.

Task 9.2 Add a circle n command to Pogo, which draws a circle of radius n at the turtle position.

Task 9.3 Add a second procedure to pogo.pro (perhaps called pogo_run_file) that is passed the name of a file as an argument, and instead of taking input from the user it reads commands from the file. Then implement a new command run file (in both pogo and pogo_run_file) which calls pogo_run_file passing the name of the file. The user can then specify their own pogo commands, for instance square.pogo and square_rose.pogo: square.pogo draw 50 turn 90 draw 50 turn 90 draw 50 turn 90 draw 50 turn 90

square_rose.pogo run square.pogo turn 30 run square.pogo turn 30 run square.pogo turn 30 run square.pogo turn 30 run square.pogo turn 30 run square.pogo turn 30 run square.pogo

Steve Milan, September 2005 59 IDL for IDLers

turn 30 run square.pogo turn 30 run square.pogo turn 30 run square.pogo turn 30 run square.pogo turn 30 run square.pogo turn 30

Pogo> run square.pogo Pogo> run square_rose.pogo

Steve Milan, September 2005 60 IDL for IDLers

§10. Programming tasks

The following tasks are envisaged as an open-ended set of examples in which to prac- tice what you have learnt in the workshop. Do these tasks in whichever order you please.

Task 10.1 Write a program to create a simple cellu- lar automaton. This comprises a row of “cells”, each of which can be either alive or dead (0 or 1). The cells die or are brought to life in successive generations depending on the number of neighbours they have: this is a one-dimensional ver- sion of the Game of Life, with which you may be familiar.

An example is shown on the right. This represents a row of 200 cells which are either alive (black or 1) or dead (white or 0). In the top row there is only 1 alive cell, located near the middle. The next row down is the second generation. At each cell position, say the jth, the number of alive cells is counted in the three posi- tions j-1, j and j+1. If none are alive the cell continues to be dead in the next gen- eration. If 1 or 2 are alive then the cell becomes (or remains) alive. If all 3 are alive then the cell dies due to over- crowding. These rules keep being ap- plied in successive generations, 800 in all in the figure. This shows how simple rules can lead to surprisingly complex (or chaotic) behaviour.

Hint: Your program should create an array 200×800 (say) in size. Set the middle element of the first row to 1. Loop through the 200 cells, following the procedure descibed above, filling in the second row with the re- sults from the first. Repeat through successive rows.

For more information on cellular automata, see: http://math.hws.edu/xJava/CA/CA.html

Steve Milan, September 2005 61 IDL for IDLers

Task 10.2 Write a program to draw the (right). This represents the complex or Argand plane, running from -2 to 2 in the x direction and -2i and 2i in the y di- rection. Each point in the plane then rep- resents a number µ = x + iy. To calculate the set, the complex variable z is set to 0 + 0i, and the following equation repeated- 2 ly iterated: zn+1 = zn - µ. For each value of µ, z has one of two fates: it either grows such that |z| → ∞ (there is an at- tractor at infinity, in chaos-speak); or |z| remains always less than 2, in which case it belongs to the Mandelbrot Set. Once |z| exceeds 2, it reveals that it is heading to The Mandelbrot Set infinity, the calculation stops, and the pixel is colour-coded with the number of iterations taken. Points close to the boundary of the set might take many iter- ations to reveal which direction they’re going in so an upper limit is usually set and the calculation stopped at this point, and the pixel again colour-coded.

The Set on the right was calculated in an array of 400×400 elements, with a maximum of 25 itera- tions. The elements were then filled with the number of steps performed until |z| > 2 or the maximum number of iterations is exceeded. tv() was used to output the results.

The program should allow the user to “focus in” on smaller portions of the complex plane to reveal the fractal nature of the Set. A Julia Set for µ = 0.0 – 0.7i

Julia Sets are very similar, expect that the initial value of z is set by the position of the pixel in the complex plane, i.e. z = x + iy, and the user sup- plies µ. The one on the right was calculated for -1 < x < 1 and –i < y < i, with µ = -0.7i.

Steve Milan, September 2005 62 IDL for IDLers

Task 10.3 Use the Euler integration method to write a program to simulate two bodies in orbit about each other. The equations of motion of two bodies of mass m1 and m2 are dv Gm m dv Gm m 1 = 1 2 2 = 1 2 m1 2 rˆ12 and m2 2 rˆ21 , dt r12 dt r21 where v1 and v2 are their instantaneous velocities and r12 is the vector pointing from body 1 to body 2, i.e. r12 = (x2 – x1, y2 – y1) if (x1, y1) is the position of body 1, etc., and the Gravitational Constant G = 6.67×10-11 m3 kg-1 s-2.

In principle, numerical integration is not necessary for this problem as the trajectories of two bodies in orbit can be found analytically. However, for three or more bodies an analytic solution is not possible. Extend the program to allow interactions between N mutually-orbiting bodies (the N-body problem). Now, the force acting on each body is the vector sum of the gravitational attraction from all the other bodies: dv n Gm m i = i j ˆ mi ∑ 2 rij . dt j=1, i≠ j rij

The figures below show the orbital trajectories of 6 bodies, ranging in mass from 1 M (the central body) to 0.0001 M. The simulation was carried out in two dimensions only. The bodies were started at random locations along the +ve x-axis, all with random velocities in the +ve y-direction in the range 10 to 20 km s-1. As the velocities were random, it is possible that the centre of mass of the system could drift out of the plot window, so before iteration began the velocity of the centre of mass of the system was found and subtracted from each velocity (essentially, translation to a frame moving with the centre of mass). The randomu() function provides a random number between 0 and 1. Your code need not be random. A time step of 1 hour was used. The simulation ended when one of the bod- ies was thrown out of the system, usually by a close-encounter with a larger body (sling-shot). Most systems are unstable and rapidly eject one of the smaller bodies; perhaps 1 in 20 leads to a relatively stable system like those below. Despite the random initiation, planet-moon systems appear frequently (below right).

Steve Milan, September 2005 63 IDL for IDLers

Task 10.4 Plasma physics courses will have revealed that charged particles in magnetic and elec- tric fields, B and E, move under the influence of the following equation of motion: dv m = q(E + v × B), dt where q is the particle charge, m the mass, and v its velocity. Use the Euler integra- tion method to trace the trajectory of an electron in an electric field of E = (0, 2, 0) µV m -1 and B = (0, 0, 10) nT, if its initial velocity is v = (0, 1, 0) km s-1 (top figure). In addition, show that |v| does not change and find the magnitude of the average drift in the x direction (the E×B direction). Compare the expected gyrofrequency and radius of gyration with that computed by the Euler method. From the lectures: qB mv E Ω = , r = ⊥ , V = . m g qB E B The code will then allow the motion in any magnetic and electric field configuration to be traced, for instance in which E = 0 and B points in the z direction but its magni- tude is a function of y (grad B drift). Ultimately, the motion in a dipole magnetic field such as that of the Earth can be found, where electrons and protons form the Van Al- len belts (bottom figure).

(A dipole field can be calculated from µ B(r) = 0 (3(m ⋅rˆ)rˆ − m), 4πr 3 where m is the dipole moment; for the Earth, m = (0, 0, -8×1022) A m2, if the magnetic axis is taken to be aligned along z.)

Steve Milan, September 2005 64 IDL for IDLers

Task 10.5 (i) Write a program to randomly create a maze. Several maze creating algorithms ex- ist (e.g. see http://www.astrolog.org/labyrnth/algrithm.htm for a description of some of them). One of the most straight-forward is known as the “recursive backtracker” algorithm.

The maze starts as a grid of n×m cells, each having 4 walls to the N, E, S, and W (this could be represented as a 3D n×m×4 array where the last dimension holds 1s or 0s in each of its 4 elements to represent a wall or a passage; alternatively, a 2D n×m array could hold values between 1 and 15, where 1 represents a wall to the N, 2 to the E, 4 to the S, and 8 to the W).

This method also uses a construct known as a “stack”, common in computer programming for tackling problems where the solution can branch, for instance at junctions within the maze. A stack is an array that holds temporary information while the algorithm is progressing. It is a “last-in, first-out” or LIFO , as will become apparent. In the present case, the stack will hold the x, y positions of cells as they are processed. The stack starts as an empty array with as many elements as there are cells in the maze (in this case a 2D array where the first di- mension has as many elements as the maze has cells, the second has 2, to hold x and y). A “stack pointer” keeps a count of how many items have been “pushed” onto the stack, pointing to the next available empty element. As an item is pushed onto the stack, it is placed in the next empty element in the stack array, and the stack pointer is in- cremented by 1. As an item is removed from the stack (“popped”) then the stack pointer is decremented by 1. (See below for examples of pushing and popping items on a stack.)

Starting in the first cell (e.g. x = 0, y = 0), count the number of “uncarved” cells (cells that still have 4 walls) in the 4 directions N, E, S, and W, that is (x, y+1), (x+1, y), (x, y-1) and (x-1, y), respectively. If there are one or more uncarved cells available then: 1) add (“push”) the current cell (x, y) onto the stack, 2) select one of the available cells at random, 3) remove the adjoining wall(s) between the two cells (if you are carving to the north you have to remove the N wall of the current cell and the S wall of the new cell), 4) move to the new cell, i.e. update your (x, y) position. If no uncarved cells are available, “pop” the last cell off the stack, i.e. update your (x, y) position to the last cell placed on the stack, and remove it from the list. Repeat. The maze is complete when there are no cells remaining on the stack.

(ii) Write an algorithm to solve the maze you have created. A variant of the recursive backtracker can be used.

Steve Milan, September 2005 65 IDL for IDLers

Start with an empty stack, the pointer pointing to the next available element:

Stack: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ... Pointer: 0 --^

Push (0,1), (2,3), and (3,1) onto the stack, and update pointer:

Stack: 0 2 3 0 0 0 0 0 0 0 0 0 0 0 0 ... 1 3 1 0 0 0 0 0 0 0 0 0 0 0 0 ... Pointer: 3 ------^

Pop the last value off the stack: return the value (3,1) and update pointer:

Stack: 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 ... 1 3 0 0 0 0 0 0 0 0 0 0 0 0 0 ... Pointer: 2 ------^

Push 3 more values onto the stack, (4,4), (3,2), and (6,1), again updating pointer:

Stack: 0 2 4 3 6 0 0 0 0 0 0 0 0 0 0 ... 1 3 4 2 1 0 0 0 0 0 0 0 0 0 0 ... Pointer: 5 ------^

Pop 5 items off the stack: return the values (6,1), (3,2), (4,4), (2,3), and (0,1),resulting in an empty stack:

Stack: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ... Pointer: 0 --^

Steve Milan, September 2005 66 IDL for IDLers

Appendix

This list is not intended to be exhaustive, but to give some idea of the most commonly used functions and commands. More details on each can be found in the IDL refer- ence manual, essential bed-time reading.

Functions where() tvrd()

Mathematical sin() cos() Commands tan() asin() Logic and loops acos() atan() if...then...else... sqrt() if...then begin...endif else begin exp() ...endelse alog() case...of...endcase alog10() for...do... for...do begin...endfor while...do... String while...do begin...endwhile strarr() string() Input and output strtrim() strlen() print strmid() openr str_sep() openw str_pos() readf printf close Variables and arrays free_lun fix() long() Plotting float() plot double() oplot complex() contour imaginary() erase intarr() window lonarr() xloadct fltarr() loadct dblarr() tv indgen() tvscl longen() tvlct findgen() write_png dindgen() Others Others help sort() stop total() pro...end n_elements() function...return...end min() max() randomu()

Steve Milan, September 2005 67