Monday, April 23, 2007

Teaching the Monkey to Dance: Scripting AIPS #2

In the first installment of this thrilling saga, I talked about how to name your AIPS scripts and load them into AIPS. Now, we're going to get into the nitty gritty details of how to write your own AIPS scripts.

In AIPS scripts, you have access to all the usual AIPS commands and tasks like listr, calib, and imagr. You also have access to some basic programming tools. To see exactly what you have in your toolbox, type help popsym (POPS is the name of the AIPS scripting language). I've replicated the contents of the popsym help files below for your enjoyment:

Help on POPSYM in AIPS version 31DEC04
Type: Symbols used in the POPS interpretive language


----Arithmetic expressions

+ A + B Add the expression A to B
- A - B Subtract the expression B from A
* A * B Multiply the expression A with B
/ A / B Divide the expression A by B
** A ** B Calculate A to the power B
( ) (A+B)*C Grouping expressions as desired
= A = B Store the value of B into A
, A = 3,5,4 Separator of elements in an array
~ A(i) ~ 1,2,3 Store values in A(i),A(i+1)...
(change only as many as on RHS)
: TO Equivalent to the verb TO
; Separator between AIPS statements

----Logical expressions

> A > B A greater than B
< A < B A less than B
= A = B A equal B (numeric or string)
>= A >= B A equal to or greater than B
<= A <= B A equal to or less than B
<> A <> B A not equal to B (numeric or string)
! A ! B A or B
& A & B A and B
^ ^ A not A

----String expressions

!! A !! B string = string A followed by string B
SUBSTR SUBSTR(A,i,j) string = chars i through j of string A
LENGTH LENGTH(A) position last non-blank in A
CHAR CHAR(A) convert number A to string
VALUE VALUE(A) convert string A to number

----Looping constructions






----Built-in functions

ATAN Arctangent (one argument)
ATAN2 Arctangent (two arguments)
COS Cosine (degrees)
SIN Sine (degrees)
TAN Tangent (degrees)
EXP Exponential
LN Log base e
LOG Log base 10
SQRT Square-root
MAX Maximum i.e. X = MAX (A, B)
MIN Minimum i.e. X = MIN (A, B)
MODULUS Root-square sum of two arguments
MOD(A,B) A - (A/B) * B i.e. remainder of A/B
CEIL(A) Lowest integer >= A
FLOOR(A) Highest integer <= A

----Procedure building verbs

PROC PV Begin building a procedure
PROCEDUR PV Begin building a procedure
LIST pV List a procedure
EDIT PV Edit a procedure
ENDEDIT PV End editing a procedure
ERASE PV Delete line(s) of a procedure
MODIFY PV Modify a line in a procedure
RETURN V Last statement in a procedure
FINISH PV End procedure building

----Variable declarations

SCALAR pV Declare scalars
ARRAY pV Declare arrays
STRING pV Declare strings

----Input/Output functions

PRINT V Print the following keyword value(s)
TYPE V Print the following keyword value(s)
READ V Read value(s) from terminal after # prompt

----Other information

CORE pV Amount of core left in POPS
COMPRESS PV Compress the core area, recovering lost space
and acquiring any new vocabulary
CLRTEMP V Clear the temp data array
DEBUG pV Debug: turns on compiler debug information
DUMP V Dump K array on terminal screen
SCRATCH PV Remove procedures in POPS
$ PV Makes rest of input line a comment

As you can see, you have most of the basic programming tools like if/then statements, for loops, mathematical functions, and comparison operators. The only non-useful bits are in the "Procedure building verbs" section. Some of the verbs in this section (list, edit, endedit, erase, and modify) are left-over from the bad old days and were used for writing procedures using AIPS built-in line editor. Trust me, you don't want to use this editor. Just write your scripts outside AIPS in your favorite text editor and load them into AIPS.

Now that you know what tools you have to work with, let's move on to how AIPS scripts are actually structured. In general, an AIPS script has two parts. The first part declares all the variables one is going to use in a script, while the second part is the actual script itself. Note that you can have more than one script (declaration plus code) inside the file you created in part 1. The convention is to group scripts that work together in one file. For example, my custom calibration routines contain a declaration section, a calibration script, and a script to nuke the current calibration. Start each section with proc scriptname, substituting the name of your script for scriptname and end each section with return; finish.

Looking at the pops language commands, you only three options for variables. (No advanced data structures for you!) Scalars are just numbers, while strings are just letters. Arrays can either be a collection of numbers or letters. To declare a scalar, you just say scalar anumber. Strings need to have their length declared (just like Fortran): string*8 aname. This creates an eight character string. Now declaring an array is slightly tricky. First, you decide what sort of array you want, then you append a number telling AIPS how big to make the array. To create an array of 15 strings, you say string*8 obj_name(15). Variable names are limited to about nine characters, so choose them wisely!

Now to remember what you were thinking when you wrote a script originally you can put an asterisk (*) at the beginning of any line to indicate that the entire line is a comment. You can also put a dollar sign ($) anywhere in a line to indicate that the rest of the line is a comment.

Note that AIPS does NOT pay attention to the first line of a script file, so always start your files with a comment (put a * on the first line). In addition, AIPS gets cranky if you have any tab characters in your script. (This may just be an issue relating to how my editor of choice, emacs, stores files.) If everything looks fine in your script and AIPS still won't read it in, look for tabs. Usually if you move the cursors over the line of code with your arrow keys you can see where the tabs are because the arrow keys move the cursor more than one space.

Well, that's enough for now. Next time, I'll go through a simple example demonstrating everything I talked about in this entry.


Laura said...

Thanks Amanda! Very handy tips. :)

DonkeyPuncher said...

Anybody know how to do nested procedures in AIPS? It is suggested in the AIPS helpfile for RETURN. Example of desired usage (quasi-pseudocode):

proc test1
scalar z
CALL test2
CALL test2

proc test2
type z

DonkeyPuncher said...

Scratch that. I figured it out. You just have to be careful in your order of procedure declaration in the runfile. For example, test2 should have been declared before test1 in my previous post. This would work:

proc test1
scalar z
type z
return; finish

proc test2
scalar z
return; finish

Unknown said...

This scripting stuff is very useful, but I can't find scripting AIPS #3 and etc. Is it out there?

I have lots of files to input, run sdgrid on, then write out. I have no idea how I could go about making sure stuff gets in the right slot to work for any given task.



Anonymous said...

I had a question for you guys:

I was writing a script to run imstat on several hundred images, matching the central RA value in the images (from the header) to a value in an array I defined elsewhere. This required me to round the header RA to 3 decimals by the following:


So for a number (say 321.504335) i should get 321.504. However I noticed that AIPS stores this value as 321.5039978. This, for obvious reasons, is causing problems.

Is there any way get AIPS to store this value correctly?

amanda said...

Hi Anonymous,

This looks like a potential bug in AIPS (or in your installation). You should probably contact the AIPS help desk using