In this project we will hopefully accomplish four goals;
1. Study simple algorithms for solving second order partial differential equations,
2. execute the algorithms with extensive use of array variables, pointers, and functions that process
arrays,
3. begin a study of computer graphics and animation using third-party graphics libraries,
4. manage a programming project with make.
Heat flow is a diffusive process that obeys the differential equation




We will write two routines that will a; find Tnew[n] from T[n], and b; swap Tnew[n] ↔ T[n] to
prepare for the next iteration. These routines will be void and will accept pointers to the arrays.
We will offset the arrays so that space is divided into 2N + 1 discrete points and x = 0 will have its
temperature stored in T[N];
float T[MAX], Tn[MAX];
... for(t=1;t<steps;t++){ update(T,Tn,coeff1, coeff2,MAX); swap(Tn,T,MAX); } ... void update ( float *Told, float *Tnew, float c1, float c2,int size){ int i,M; M=(size-1)/2; for(i=-M+1;i<=M-1;i++){ *(Tnew+i+M)=c1*(*(Told+i+M))+c2*( *(Told+i+M-1) + *(Told+i+M+1)); } /* now get the ends */ *(Tnew+0)=c1*( *(Told+0)) + c2*( *(Told+1)); *(Tnew+size-1)=c1*( *(Told+size-1)) + c2*( *(Told+size-2)); } void swap ( float *Told, float *Tnew, int size){ int i,M; M=(size-1)/2; for(i=-M;i<=M;i++){ *(Tnew+i+M)=*(Told+i+M); } } |
In order to use the library one first compiles and installs it, and then includes its header file in the program preamble
#include<stdlib.h>
#include<math.h> #include<plot.h> |
and link with the library, as well as Xt, X11, ICE, Xext and several other Xwindows libraries
gcc stepwave.c -lplot -lXaw -lXmu -lXt -lSM -lICE -lXext -lX11 -lm
|
One of the variables that needs to be declared in order to use the library is a file descriptor for the plotter device, be it GIF, X, PS or one of the other choices. We will create animated pseudo-GIF89a movies, output to the Xplotter or create PostScript graphics with the three devices listed above, or else use the PNM device to create pnm files suitable for encoding into an MPEG stream. At any rate the preamble of our program now looks like
#include<stdio.h>
#include<strings.h> #include<stdlib.h> #include<math.h> #include<plot.h> int pl_handle; /* plotter file descriptor */ double firstX, firstY, secondX, secondY, int n,m,l,time; |
Libplot is extremely sophisticated in how it sets up a plotting window using affine or
vectorized graphics in a user-defined coordinate system. We set up the plotting device by
specifying its parameters such as size, type, backround color, transparency, and so forth,
and then open the plotter by initializing its file descriptor with pl_newpl(). The first
argument passed to it is the plotter device, “X”, “gif”, “ps”, “pnm” are some of your
options.
For example we take the following steps to open an animated GIF plotter with a 5 ms frame delay
and transparent white background. This will make any white objects drawn in the plotter
transparent, so that when viewed in a web browser, which knows how to load GIF89 images, the
background does not appear.
fptr = fopen ("well.gif", "w+"); /* GIF written to this file */
pl_parampl ("BITMAPSIZE", "300x150"); pl_parampl ("BG_COLOR", "white"); pl_parampl ("TRANSPARENT_COLOR", "white"); pl_parampl ("GIF_ITERATIONS", "100"); pl_parampl ("GIF_DELAY", "5"); pl_handle = pl_newpl ("gif", stdin, fptr, stderr); /* actual initialize */ pl_selectpl (pl_handle); /* select this plotter for output */ if (pl_openpl () < 0) /* open Plotter */ { fprintf (stderr, "Couldn’t open Plotter\n"); return 1; } |
All of the plotters parameters must be set with pl_parampl() commands before the file
descriptor is declared (the plotter device is opened).
The code block above opens a gif plotter with our desired parameters, with output into file fptr
and error output to stderr, which is the console. We could declare several plotters in the same
program and switch between them with the pl_selectpl() command. To open the Xdisplay
instead and double buffer, or build the next image while displaying its predecessor,
with
pl_parampl ("BITMAPSIZE", "300x150");
pl_parampl ("BG_COLOR", "white"); pl_parampl ("USE_DOUBLE_BUFFERING", "yes"); pl_handle = pl_newpl ("X", stdin, fptr, stderr); pl_selectpl (pl_handle); if (pl_openpl () < 0) /* open Plotter */ { fprintf (stderr, "Couldn’t open Plotter\n"); return 1; } |
This is the initialization that we would use to perform a real-time animation in Xwindows. For
truly numerical-intensive simulations, real-time animation could be painfully slow, and in those
cases we use the GIF89a devices, or output snapshots of the action as PNM graphics to be encoded
into an MPEG stream.
Once the plotter has been initialized we set up an affine coordinate system within it with the command pl_fspace(). This takes four floating point arguments, the (x,y) coordinates of the lower left corner of the plotting window, and the (x,y) coordinates of the upper right corner. For example, suppose that we have created and stored a large set of data points ı
![(xn,yn) = (point[n][0],point[n][1])](heat4x.png)
printf("\tDetermining the scale of the snapshots;\t");
largest=point[0][0]; for(n=0;n<N;n++){ if(point[n][0]>largest)largest=point[n][0]; if(point[n][1]>largest)largest=point[n][1];} pl_fspace (-1.1*largest,-1.1*largest,1.1*largest,1.1*largest); |
There is no need to take into account aspect ratio if the X and Y-limits of the window are of the
same size.
We now specify various graphics primitive attributes such as linewidths, text sizes, object colors and fill types. The following example illustrates a variety of these attributes and the commands used to set them
pl_filltype (1); /* fill in the object */
pl_pencolorname ("red"); pl_fillcolorname ("red"); pl_fcircle(0.0,0.0,0.04*largest); /* draw the nucleus */ /* centered at (0,0) */ /* radius is 4 percent */ /* of coordinate window */ pl_fmove(-0.9*largest,-0.9*largest); /* move to this location in window */ pl_ffontsize(0.1*largest); /* textheight 1/20 window width */ pl_label("3,2,0"); /* and draw this label string */ pl_filltype (0); /* subsequent objects not filled */ |
The actual routines to create graphics primitives all begin with pl_, and come in two flavors, we want to use those that begin with pl_f, which will place objects at locations whose coordinates are floating point numbers, rather than integers. We can create lines, points, circles, ellipses, boxes, bezier curves, labels and a variety of other objects. We can specify colors with commands such as
pl_pencolorname("gray60");
pl_pencolor(55000, 65000,34000); pl_fillcolor(0,0,65535); pl_filltype(1); /* fill=yes */ pl_linemod("dotted"); pl_linemod("solid"); pl_fillcolorname("yellow"); pl_ffontsize(0.35); pl_fontname("HersheySerif"); |
and so forth. For the full list of commands and syntax, read the info page
less /usr/info/plotutils.info
or less /usr/local/info/plotutils.info |
The final act in a libplot program is to close the plotter device and de-allocate its file descriptor with
pl_closepl ();
pl_selectpl (0); pl_deletepl (pl_handle); |
You will find it extremely easy to program high quality graphics and animations in X with this
library. I recommend the 2.2 version plotutils-2.2.tar.gz over other revisions.
Make is a utility for automating the building and re-building of large programming projects that
contain many sources. If you have placed all of your source-files ( *.c files) in a directory, and type
“make” in that directory, make will look for a special file, called the Makefile, that consists of
substitutions or identifications, targets, dependencies and directives, and execute the
instructions in the file to build the project.
The advantages to using make are many; you can define long compiler invocations in the
Makefile rather than type them in at compile time, you can automate compilation of a project
that contains thousands of C source files, and if you change one or two of your sources, make will
re-compile only those sources that depend on the files that you changed, rather than re-compile the
entire project.
Here is the Makefile for the four versions of our heatflow project;
# Simple C Makefile for 290 projects
CC = gcc CPP = /lib/cpp FLAGS = -Wall -fomit-frame-pointer -O2 LDFLAGS = -I./ -L./ -lm XFLAGS = -L/usr/X11R6/lib -lplot -lXaw -lXmu -lXt -lSM -lICE -lXext -lX11 -lm # targets all: heatflow1 heatflow2 heatflow3 heatflow4 \ heatflow1: heatflow1.c $(CC) $(FLAGS) heatflow1.c $(LDFLAGS) -o heatflow1 heatflow2: heatflow2.c $(CC) $(FLAGS) heatflow2.c $(LDFLAGS) -o heatflow2 heatflow3: heatflow3.c $(CC) $(FLAGS) heatflow3.c $(XFLAGS) -o heatflow3 heatflow4: heatflow4.c $(CC) $(FLAGS) heatflow4.c $(XFLAGS) -o heatflow4 clean: -rm heatflow1 heatflow2 heatflow3 heatflow4 |
Make is very sensitive about syntax; the dependency-directive pairs have the directive
lines beginning with a tab, not eight spaces. If you get a “missing seperator” error
from make, check your file for tabs. I find that the vi editor is the best one for writing
Makefiles.
To build all of the targeted executables, type “make” or “make all”, to build just one target, say
heatflow2, type “make heatflow2”, and to remove stale compiled targets for a fresh build, type
“make clean”.
Lines that begin with # are comments, and are ignored. Values of defined objects or substitutions are referred to by prepending the substitution with a dollar sign, just like you would do in a shell script.