Advanced Programming Topics 10 December 1991 1.) Introduction 2.) Why break a program up? 3.) Breaking one program up into several files 4.) Breaking one program up into several programs 5.) A simple example: the BLAST program 6.) Redirecting the standard input and output 7.) Doing more with input and output using data files 8.) Using the FORTRAN statements OPEN and CLOSE to create a file 9.) How to read information from an "old" file 10.) Dealing with files of varying size 11.) Using data files with the BLAST program 12.) The INTERNET, and the programs that let us move around on them. 13.) How to move a program to the Cray, compile, and run it. 14.) Running the BLAST program on the Cray 15.) What to do about graphics? 16.) Exercises * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 1.) Introduction I hope you have all had a chance to begin writing programs in FORTRAN on your workstations. The information I gave you in August was very limited, and it's time to discuss some matters appropriate for advanced programming. I will try to focus on the important ideas, and explain WHY we want to do certain things. In particular, I want to suggest some ways in which you can try to manage your programming project, including: breaking a program up into separate subroutines, files, or programs. storing and using information in data files moving your programs and data from one machine to another * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 2.) Why break a program up? In the examples I gave you in August, we always worked with one file, and that might seem at first to be a perfectly reasonable model to stick with. But even in those examples, I frequently used subroutines. Remember some reasons for subroutines? I could "isolate" one part of the computation from others. This helped me to focus on which data items were to be worked on by the subroutine, and on what the subroutine had to do. It also encouraged me to make that subroutine do a single, clear task, which could be written up. Also, I could easily "cannibalize" subroutines from one program to use in another program. In a similar way, as a single program gets larger and larger, it can be difficult to understand what it does, to remember what variable names you've already used. It can be hard to make a single program do everything you want to. You're much better off trying to break a project up into a multitude of "tools". Then you can happily modify one tool without having to worry about breaking all the other ones. Another reason to consider breaking your program up is simply that bigger programs take longer to edit, print, and compile. If you notice that editing has become slow, because it's a long way from top to bottom in your file, or if compiling takes long enough that you go out and get a cup of coffee while waiting, maybe it's time to think about cutting the program up. Yet another reason you might want to consider breaking a program up is that it makes group efforts easier. If a team of students can agree on a specification of a subroutine's argument list, or on the the format for the data file that one program writes for another program's use, then the students can work independently on separate sections of the project, without interference. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 3.) Breaking one program up into several files One simple way to cut a program up is to break it up into groups of routines. For instance, if my original program file contained a main program, and subroutines BOB, CAROL, TED, and ALICE, then I might arbitrarily break it up into three files: main.f, containing the main program, men.f, containing BOB and TED women.f, containing CAROL and ALICE. By the way, there's no need for the file names to correspond in any way with the names of the routines inside them! Then, instead of compiling using the command: f77 myprog.f <-- compile and load the file. I would have to compile using the command: f77 main.f men.f women.f <-- compile and load all files. Now, suppose that subroutine TED still needs to be worked on, but all the other parts of the program are rarely changed. Then, I can also use this method of breaking up the program to save me recompile time. I first have to know about the "-c" switch, which says, "compile, and save the compiled code, but don't make a single executable". If I do that for my example, I would type: f77 -c main.f men.f women.f <-- compile all files. and I would discover that instead of having a single file "a.out", I had three files, "main.o", "men.o" and "women.o". These are "object" files. These files are the main work of the compiler, but they are usually thrown away as soon as the executable "a.out" is created. I can go ahead and make an executable file out of them by issuing this command: f77 main.o men.o women.o <-- load all files. which should work much faster than the very first compile command I gave, because now we can skip the main compiling step. The F77 compiler realizes that the files with names ending in ".o" have already been compiled, and jumps to the second, "linking" phase. Now actually, we haven't save any time yet. But now let's make a change to the TED routine in the MEN.F file. Notice that the editor works faster because MEN.F is smaller than MYPROG.F used to be. Now we're ready to remake the executable program. But here's the switch. We type: f77 main.o men.f women.o <-- recompile men.f, then load all. which says "recompile the MEN.F file, but use the old MAIN.O and WOMEN.O files, since their source codes haven't changed.". We end up with an "a.out" file much more quickly. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 4.) Breaking one program up into several programs You can also make your life easier by breaking one program up into several independent programs. A number of benefits can occur. For instance, if I modified the first program, I'd only have to recompile that program, but none of the others would have to be touched at all. Another important feature of breaking a program up comes because you have to store the results of one program for use by another. We will see in a minute that we do this using FORTRAN data files. But the side benefit of this technique is that you can take a "peek" at the information that your programs are working on, which might be helpful for debugging. And you can even write new little tools to analyze this data, without destroying it. For instance, if I knew that a data file had a list of 100 real numbers, I could write one program that produced the maximum entry, another that produced the average, another that sorted the entries. Each of these programs could be written separately, and yet all would "share" the same data in a very cooperative way. And it is much easier to write good small programs than one huge one. For our work, we will take a very simple model of breaking a program up. We'll assume that we need to have three programs: A) An input program. This program "talks" to the user, asking it to specify information about the problem to be solved. Questions might include "What angle should the cannon be aimed", or "How much does the cannonball weigh?". We'll try to make this program friendly, and have it check input for correctness. When the program is done, it will store the results in a file. B) A computational program. This program takes the data file created by the input program, and uses it to define the problem to be solved. As soon as the data is read in, the program works at solving the problem, and writes the results to a file. The program probably does not print much information out, and could almost be run without human intervention. C) An analysis program. This program takes the results from the computational program, and offers to analyze them in various ways, under the guidance of the user. Analysis might include plots, or simple printed tables. I hope you will see that it would be very difficult to write a single program that did all of this work! The way we've described this particular model, it should be clear that we're hoping we can run the computational program on the Cray! That means we're going to have to understand how to create data files, how to move them to another machine, and how to do things on the Cray that we're used to doing on our workstations. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5.) A simple example: The BLAST program I'd like to discuss how ideas I've talked about would be implemented. As our example, I'd like to consider the idea of modeling the behavior of a cannonball shot from a cannon with a certain speed and direction. Actually, I'd like to specify the amount of gunpowder, and the angle, in degrees. I think I'd like the position and velocity of the cannonball for every second of flight. The only external force will be gravity. I'd love to add in wind resistance, but I'll do that later! My input program will be very simple, and just take the gunpowder and angle values, converting the angle from degrees to radians, which is what FORTRAN prefers. My computational program will receive that data, compute the location of the cannonball at each second, and create a set of results. My analysis program will read the results, and offer to print them, or to plot all the positions on one graph, or to try to do a set of snapshots of the position that would be suitable for an animation. What this program actually does will be determined by the user. It's clear that if I add wind resistance later, that that information should be specified to the input program, stored in the data file, and used by the computational program. But I can wait til I get my basic program working. The important thing is, that now I have an outline in my mind of how I want to arrange the computation. We'll see soon how data files will be used to help me reach my goal. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 6.) Redirecting input and output All programs that you write automatically use two special files, called "STANDARD INPUT" and "STANDARD OUTPUT". For instance, if you use statements like WRITE(6,1000) or WRITE(*,*), you are having the program store information into the "STANDARD OUTPUT" file, although we prefer to think we're just printing stuff out. Suppose, however, that you wanted to save the results of running your program. This might be because you want to examine them later, or mail them to someone, or even "feed" the results to another program. On machines using UNIX (which includes your workstations, and our Cray computer), it is very easy to have information stored into a file rather than printed on the screen. Doing so is called "REDIRECTION". To make the output of a program go into a file instead of appearing on the screen, you simply add some information to the execution line, namely, a greater-than sign, and the name of the file you want to use, like so: a.out > myresults.dat Now, of course, you won't see your output coming onto the screen, so you won't know what's going on with your program until you see the STOP statement, or a prompt from the computer. This can be a disadvantage, but we'll see how to deal with this in a moment. Now it's also true that after you've run a program a few times, you may know the exactly sequence of input you want to use. As a simple example, suppose you have a program to compute the average of a set of numbers, which expects to be told how many numbers are to be averaged, followed by the numbers themselves. Suppose it's important that you type these numbers exactly right; or suppose for some reason you need to repeat the calculation several times, with the same input. Or suppose the information is already sitting in a file somewhere else. Rather than having to actually type these values "live", you can tell your program to expect to find them in a file, simply by modifying the execution line to include a"less-than" sign, followed by the name of an input file, as in this case: a.out < grades.dat In this case, you'll see all the messages that the program would normally print, encouraging you to enter the number of values to be averaged, and the values themselves, but the responsibility for entering those quantities is now up to the file GRADES.DAT, and not to you. As long as that file has the proper information, in the proper order and format, your program will never know that it's getting "canned" data rather than fresh. By the way, you can do both tricks at the same time; that is, you can feed your program data from a file, and store its output into another file. To do so, you have to list the input file first, and then the output file, such as in this case: a.out < grades.dat > averages.dat Remember that what we have said so far only applies to the ways that a program reads data using a statement like READ(5,1000) or READ(*,*), and writes data using a statement like WRITE(6,1000) or WRITE(*,*), involving the special "STANDARD INPUT" and "STANDARD OUTPUT" files. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 7.) Doing more with input and output using data file FORTRAN data files are an important programming tool. Using data files, you can: Have a program read information prepared earlier by another program. Transfer information from one computer to another. Avoid having to recompute certain information. We will only talk about the simplest kind of FORTRAN data file, which is called "SEQUENTIAL" and "FORMATTED". These two words guarantee that the file can be typed out using the "MORE" command. There are other, more mysterious kinds of FORTRAN files that we will avoid. Let's start out by supposing that you already have a program which writes data to the standard output unit. That is, the program has some lines that look like this: REAL X(100) WRITE(*,*)'This is version 6 of the program.' DO I=1,100 WRITE(*,*)X(I) END DO WRITE(*,*)'We are done printing the list of values.' Maybe you need to print out all 100 values of X, but do you really want to see them on the screen? Probably not. On the other hand, the two messages are appropriate for you to see when you run the program. What we would like to do is to be able to have some output go to the screen, and some output go to a file. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 8.) Using the FORTRAN statements OPEN and CLOSE to create a file To work with files, we will need three FORTRAN statements: The OPEN statement to specify the destination for the information. A special form of the WRITE statement to send information to the file. The CLOSE statement to "close up" the file. The OPEN statement has the following form: OPEN(UNIT=number, FILE=name, STATUS=new-or-old, ERR=label) Here, the capitalized quantities should be typed as is, but you should replace the lower case items by information you choose. In particular, UNIT=number, replace "number" by any number between 1 and 99, except 5 and 6. FILE=name, replace "name" by a CHARACTER string or variable, containing the name of the file. STATUS=new-or-old, replace "new-or-old" by 'NEW', because we are going to create this file. ERR=label, replace "label" by a statement label, to which we will jump if for some reason the OPEN statement has a problem. Secondly, we must change every WRITE statement whose information we want to go into the file. To do so, we simply replace the first '*' by the unit number we specified earlier. Finally, once we are done using the file, we should issue a CLOSE command of the form CLOSE(UNIT=number) where "number" has the same value as the OPEN statement specified. As an example, here's how I would rewrite the earlier portion of code: WRITE(*,*)'This is version 6 of the program.' OPEN(UNIT=7, FILE='xvalues.dat', STATUS='NEW', ERR=20) DO I=1,100 WRITE(7,*)X(I) END DO CLOSE(UNIT=7) WRITE(*,*)'We are done printing the list of values.' STOP 20 CONTINUE WRITE(*,*)'Woops, could not open the file!' STOP If we ran this program, we would notice that we now have a new file in our directory, containing the 100 values that we used to see on the screen. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 9.) How to read information from an "old" file It's nice that we can "type out" the information in the file that we created earlier. But that's not the big payoff. The big payoff is that now the data can be used by other programs we write! Here's what a program might look like that uses the data we just created: WRITE(*,*)'This program reads the data, and averages it.' OPEN(UNIT=93, FILE='xvalues.dat', STATUS='OLD', ERR=20) AVERAGE=0.0 DO I=1,100 READ(93,*)W AVERAGE=AVERAGE+W END DO AVERAGE=AVERAGE/100.0 CLOSE(UNIT=93) WRITE(*,*)'We are done reading the list of values.' WRITE(*,*)'The average value is ',AVERAGE STOP 20 CONTINUE WRITE(*,*)'Woops, could not open the file!' STOP What's different in this example? A) I used the unit number 93 instead of 7. FORTRAN doesn't care what number I choose for a file. It simply relies on the OPEN statement to explain to it that when I ask it to READ from unit 93, I really mean the file 'xvalues.dat'. B) The OPEN statement says that 'xvalues.dat' is an OLD file. What I'm telling FORTRAN is that the file exists already, and that I'm going to ask it to read information from the file. If the file does NOT exist, then FORTRAN will know that there's a problem, and will jump to statement 20. C) Notice that when I wrote the data, it was stored as an array, X(*). Now I read the data, one at a time, and store each value in the single variable W. There's no need to use the same name for the variable that was used before. Moreover, we didn't store the information in a vector. Again, there's no need to do so if we simply want to read each value, use it quickly, and throw it away. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 10.) Dealing with files of varying size In the above example, I knew how many items I wanted to read in (100). So I was able to write the DO loop to read the correct number of items. Suppose, however, that that number varied? In the nicest case, the program that wrote the file originally could have left a "note" for this program, warning about the number of data items following. That is, the first line of the file would be an integer, the number of data items, followed by that many data lines. In our example, the file might begin: 100 17.3 101.3 and so on. Then, the "heart" of our program would be modified to be: READ(93,*)N AVERAGE=0.0 DO I=1,N READ(93,*)W AVERAGE=AVERAGE+W END DO AVERAGE=AVERAGE/REAL(N) You can see how such a simple change gives the program much more power! Now, here's a harder problem. Suppose I have to read a data file, containing a bunch of numbers, and I don't know how many there are. Can I handle it? In particular, could I average an unknown number of real numbers? In fact, you can. The key is to use a modified version of the READ statement. Each time you use the READ statement, it expects to find a new number to read. If it doesn't find such a number, it will ordinarily cause your program to stop. But if you include the string "END=label" in the statement, the READ statement will jump to the given statement label if it runs out of input. This can allow you to read an arbitrary amount of data. If you increment a counter each time you successfully read data, you can also keep track of exactly how much you've read. So let's try the averaging example one more time: AVERAGE=0.0 N=0 10 CONTINUE READ(93,*,END=20)W N=N+1 AVERAGE=AVERAGE+W GO TO 10 20 CONTINUE AVERAGE=AVERAGE/REAL(N) Do you see how this works? We read a number W, and we note that we got one more number, and we add it to the average, and start over. But as soon as we try to read another number and fail, we jump to statement 20, because there's nothing more to read. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 11.) Using data files with the BLAST program Now I think we can see that with the BLAST program, there are going to be several data files flying around. The first file to be created will contain the user input that defines the problem. In our case, this is simply the two real numbers defining the angle and the amount of powder. The PREBLAST program, which will get the user's input and create the file, also allows the user to specify the file name to be used. If you look at the text of the program, you'll see that the filename is stored in a CHARACTER variable, rather than being a quoted string. When the BLAST program begins, it asks for the name of the input file to use. The user could have prepared several such input files with PREBLAST, and so this is a convenience that allows the user to store favorite problems and run the ones of interest. The BLAST program opens the file, reads the two items of interest, and closes it again. The BLAST program opens a "new" file, which is going to contain the results of the computation. The program "fires" the cannonball, and then figures out where it is at each subsequent second, until it discovers that the cannonball has hit the ground again. The results of each step are written to the file. Notice that each line of this file contains several items of data: the time, the X and Y positions, and the X and Y velocities. Once the cannonball has hit, the results file is closed, and the program terminates. The POSTBLAST program comes along, and opens up the results file. Notice that the BLAST program stored the results as "scalars", but that the POSTBLAST program stores all the results together as "arrays". The BLAST program didn't need to have all the results at one time, but the POSTBLAST program needs all the results, so that it can print or plot them. There's actually another pair of files created by the POSTBLAST program: CGM graphics files containing either a single plot of all the positions, or a series of snapshots. As you can see, it's easy to start generating all sorts of files during the solution of a single problem! But we'll see that using files will give us new ways of using data and solving problems. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 12.) The INTERNET, and the programs that let us move around on them. By now, you should already have had some experience with the Internet. The Internet is simply an electronic network which connects thousands of computers. This connection allows people to send mail to each other, transfer files, and even log in directly to another computer. Each computer on the Internet has an address. The address includes several parts that correspond roughly to "city", "state" and "country" on a regular mail address. For instance, the Cray's address is "pscymp.psc.edu". pscymp is our own local name for the Cray. psc identifies all machines at the Pittsburgh Supercomputing Center. edu identifies all places in the "educational" subnetwork. If you are using a machine that is already in the "psc" domain, then you can use the short form of address. In that case, for instance, the Cray is just "pscymp", our UNIX front end machines are just "pscuxa" and "pscuxb" and so on. If I have an account on another machine, I may want to copy files from my current computer to the other one, or bring files from there to here. In either case, I can use FTP, the File Transfer Program. To use FTP, I have to specify the name of the other machine; for instance, to copy files to or from our ULTRIX machines, I would start the FTP program by: ftp pscuxa <--- (or ftp pscuxa.psc.edu, perhaps!) When the machine answers, I have to identify myself. It may ask: USER: myname but in other cases I have to start the identification procedure: user myname In either case, I'll be asked for a password: PASSWORD: mypassword Assuming I typed this correctly, I can now issue the "GET" or "PUT" commands. In the simplest case, I might say something like this: get letter.txt to copy "letter.txt" from the other machine to my current machine, or put sort.f to copy the program "sort.f" from my current machine to the other one. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 13.) How to move a program to the Cray, compile, and run it. At this point, we have a data file, and a program, sitting on our workstation. We need to get them to the Cray, run the program, and bring the results back to the workstation. This process will have four parts: A) We will TELNET to the Cray from the workstation. This means we will need to log in to the Cray, using our Cray username and password. Then we will move to the temporary directory, with the command "cd $TMP". telnet pscymp <-- or sometimes, "telnet pscymp.psc.edu" LOGIN: myname PASSWORD: mypassword cd $TMP B) We will use the FTP program to get copies of our program and data file transferred from the workstation to the Cray. This means we will need to give the FTP program the name of the machine where the files are, our username and password on that machine, and the names of the files we want. Once the files are successfully transferred, we may want to store copies of them into CFS. ftp mymachine <-- or sometimes, "ftp mymachine.pp.psc.edu" user myname password mypassword get mydata.dat get myprog.f quit cfs store mydata.dat cfs store myprog.f C) We will now compile the program and run it. We may have to make some last minute corrections to the program, or the data file. We will need to keep track of any error messages that occur. But if all goes well, the program will run to completion, and create an output data file for us. cf77 myprog.f a.out D) We now use the FTP program to send a copy of the newly created data file back to our workstation. This is almost exactly the same as step B, except that we will be using the PUT command rather than the GET command. ftp mymachine user myname password mypassword put myresults.dat quit * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 14.) Running the BLAST program on the Cray Let's go through the exact steps that would be required to use the Cray for the BLAST program. A) Move information to the Cray Surely, we'll need the source code for the file, blast.f. We'll need the input data file, and let's say it's called startup.dat. To get these files up there, I would do the following: telnet pscymp username: myname password: mypassword cd $TMP ftp myworkstation get blast.f get startup.dat quit By the way, if BLAST.F was not in my top level directory, but in a subdirectory, I could actually use the "cd" command to move there. For instance, I might have said: ftp myworkstation cd blaster <--- Move to the "blaster" workstation subdirectory get blast.f get startup.dat quit B) Store information into CFS for later use If I only want to run the program once, right now, I can skip this step. But it's usually a good idea to store permanent copies of my files into CFS before proceeding. That way, I can log out and come back later and get my files. And this will also make it possible to run batch jobs. In my case, the only commands necessary are: cfs store blast.f cfs store startup.dat C) When you're ready to run, retrieve stuff from CFS Again, you can skip this step if you've just copied your files up from your workstation. Otherwise, you'll want to pull copies of your files out of CFS now using these commands: cfs get blast.f cfs get startup.dat D) Compile and run the program Remember that the FORTRAN compiler on UNICOS is called CF77, not F77! Type these commands: cf77 blast.f a.out The blast program will ask us for the name of the input file to read, and the name of the output file to be created. I'll call mine "results.dat". When the program's done, we'll store a copy of the results into CFS: cfs store results.dat and send another copy back to our workstations: ftp myworkstation user myname password mypassword put results.dat quit (If you want to put the results in a subdirectory, you can also use the CD command here. For instance, "cd blaster" to move to the "blaster" workstation subdirectory before copying the file.) * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 15.) What to do about graphics? In the August class, we only talked briefly about graphics. We had an example of a program that did the "Life" game, and graphed its results. Also, Anjana Kar showed you how the DRAWCGM library could be used by a FORTRAN program to create one or more images. You'll be getting more details on graphics, DRAWCGM and animations in another talk. But I want to tell you how what we've discussed so far affects what we do with graphics. Now that we've talked about libraries a little, I hope you understand the special compile statement that you have to use if you want to call DRAWCGM routines: f77 myprog.f -ldrawcgm The "-l" switch tells the compiler to look for a file named "libdrawcgm.a" in /lib, /usr/lib or /usr/local/lib, which is where general purpose compiled software libraries are stored for everyone's use. The compiler will expect to find in that library the missing routines that MYPROG called. Once we've run our program that includes graphics, what do we get? A file, of course, with a name like "cgmout.cgm". Unfortunately, this file is not a "text" file. We'll be mystified if we try to type it out using the MORE command. It's just a bunch of compressed information describing our pictures. However, since it's a file, we know how to do some things with it: If we're on the Cray, we can store it in CFS, so that it doesn't get wiped out, using a command like cfs store cgmout.cgm We can transfer it to another computer, using the FTP command: ftp mymachine USER: myname PASSWORD: mypassword put cgmout.cgm quit Finally, if we want to use the GPLOT program to display the file, we would type gplotm and pick the file's name from a menu. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 16.) Exercises A) Use redirection of input and output. i) Redirection of output is easy, as long as the output doesn't contain important messages to you. Copy the "vector.f" program from the HIFORTRAN examples: cp /usr/local/examples/ultrix/hifortran/vector.f . Compile the program and run it. You should get lots of output. Now rerun the program, but redirect the output to a file. You should get no output. Type out your file (using the MORE command) to verify that that's where your output went. ii) To redirect the input of a program, you need to know beforehand what information the program is going to want. The AVERAGE program requires you to type one number per line, and then type a final line containing the word 'END' or anything that's not a number. Copy the average program: cp /usr/local/examples/ultrix/hifortran/average.f . Compile and run it once, and have it average three numbers. Then use an editor to create a file containing the same input, and rerun the average program, but have it read from the file. iii) Can you run the AVERAGE program one last time, and have it read from your input file, and write the average value to a file instead of to the screen? B) Read a file, and beware of sudden endings! Write a program that reads and prints the first 10 lines of a file whose name is chosen by the user. Assume the line is no more than 80 characters long. Declare a single variable to hold a line as follows: CHARACTER LINE*80 and use the following lines (or something more sophisticated) to read and print a line: READ(23,'(A)')LINE WRITE(*,'(A)')LINE Once you've tested your program, see what happens when you try a file which has less than 10 lines! C) Read a file, and create a modified version of it. Copy one of the "results" files from the BLAST program. This file should contain a series of lines of data. The first two lines contain one value each, the ANGLE and amount of POWDER. All the rest of the file contains five values per line, holding values of T, X, Y, VX and VY. Write a program that will make a copy of this file which contains only the values of X and VX. D) Rewrite two programs to communicate more data Modify the BLAST program so that you can specify the time interval to be used, which is currently one second. You'll have to find where the BLAST program chooses this stepsize, and make it a variable which is communicated through a data file set up by PREBLAST. E) Try breaking a program up into individual subroutines. The FSPLIT program may be used to break a FORTRAN program up into separate files, with each file containing exactly one subroutine or function from the original program. The FSPLIT program should be available to you automatically, as part of the computer's set of utilities. It is used by typing fsplit myprog.f which will cause it to break up MYPROG.F. Be sure to carry out this work in a directory with no other FORTRAN programs! You may want to issue the following commands first: mkdir exercise cd exercise Now, copy the BLAST program and a data file from the examples directory: cp /usr/local/examples/ultrix/hifortran/blast.f . cp /usr/local/examples/ultrix/hifortran/startup.dat . Now rename it, and split it up, and throw away the original: mv blast.f blast.for fsplit blast.for rm blast.for Take a directory listing to see that you have the original file, blast.for, plus four or five files containing individual routines. Try to compile these files, and create an executable, using the "two-step" command described in section 3. Run the program. Now make a tiny modification to just one file, the main program, by changing GRAVITY from 32.0 to 64.0. Now use the special version of the compile command that only recompiles the main program, and recreates the executable. Run the program to verify that GRAVITY did change.