This program is very, very short. Here is the code.
#include <stdio.h>
#include <stdlib.h>
#include "mpe.h"
#include "mpe_graphics.h"
int main( int argc, char** argv )
{
MPE_XGraph graph;
int ierr, mp_size, my_rank;
MPE_Color my_color;
char ckey;
MPI_Init( &argc, &argv );
MPI_Comm_size( MPI_COMM_WORLD, &mp_size );
MPI_Comm_rank( MPI_COMM_WORLD, &my_rank );
ierr = MPE_Open_graphics( &graph, MPI_COMM_WORLD, NULL,
-1, -1, 400, 400, 0 );
if ( ierr != MPE_SUCCESS ) {
fprintf( stderr, "%d : MPE_Open_graphics() fails\n", my_rank );
ierr = MPI_Abort( MPI_COMM_WORLD, 1 );
}
my_color = (MPE_Color) (my_rank + 1);
if ( my_rank == 0 )
ierr = MPE_Draw_string( graph, 187, 205, MPE_BLUE, "Hello" );
ierr = MPE_Draw_circle( graph, 200, 200, 20+my_rank*5, my_color );
ierr = MPE_Update( graph );
if ( my_rank == 0 ) {
fprintf( stdout, "Hit any key then return to continue " );
fscanf( stdin, "%s", &ckey );
fprintf( stdout, "\n" );
}
MPI_Barrier( MPI_COMM_WORLD );
ierr = MPE_Close_graphics( &graph );
MPI_Finalize();
return 0;
}
Here is how to link and compile this program:gustav@bh1 $ mpicc -o cxgraphics cxgraphics.c -lmpe -L/usr/X11R6/lib -lX11 -lm gustav@bh1 $In order to run it you must undertake the following steps:
DISPLAY points to an ssh socket,
which is going to be the case if you connected to the AVIDD
cluster using slogin -X, redefine DISPLAY to point to
your physical display. Use either the IP number or the
fully domainized name of the machine from which you have made
the connection. Note that the laboratory PCs don't have fully
domainized names, so in this case you need to use their IP
numbers. To find what they are, use the netstat -rn
command in your local Cygwin environment.
xhost +. This command is a little dangerous, because
it opens your X11 display to read/write operations from any
other system. The alternative would be to list the names
of all AVIDD nodes, and then for each of them issue the
command more specific command, e.g.,
xhost +bc64.uits.indiana.edu.
mpdboot. If you had MPD running before, you should
shut it down with mpdallexit and then restart it.
$ mpiexec -n 16 cxgraphics
Now let us have a closer look at the program and see how the MPE graphics routines are used in it.
After the usual initializations:
MPI_Init( &argc, &argv );
MPI_Comm_size( MPI_COMM_WORLD, &mp_size );
MPI_Comm_rank( MPI_COMM_WORLD, &my_rank );
the program opens the X11 window by
calling
ierr = MPE_Open_graphics( &graph, MPI_COMM_WORLD, NULL,
-1, -1, 400, 400, 0 );
&graph is the pointer to an opaque
MPE variable MPE_XGraph. Once the window has been
successfully opened, other drawing routines will refer to
this graph.
"beige.ucs.indiana.edu:0.0". If instead the
name is set to NULL, every process is going to get the
name from its MPD, which, in turn, gets it from the environment
on the console node at the time of MPD booting. You should now
understand, why you cannot use the ssh socket, such as, e.g.,
localhost:27.0. This socket exists on the console node only,
but not on any other node. Consequently processes running on
other nodes would not be able to access it and function
MPE_Open_graphics would return an error.
-1 says
go as far to the left, or upwards, as you can. So the window
is going to be created in the upper left corner and it is going to
be 400 pixels wide and 400 pixels high.
MPE_Open_graphics may be used either in
the collective or in the non-collective mode. Whether it is used
in the collective mode is specified by the last argument, which is
a BOOLEAN variable. If it is FALSE, the call is
non-collective, otherwise it is collective. Here it is
non-collective, because the last argument is 0 = FALSE.
if ( ierr != MPE_SUCCESS ) {
fprintf( stderr, "%d : MPE_Open_graphics() fails\n", my_rank );
ierr = MPI_Abort( MPI_COMM_WORLD, 1 );
}
A process
that has failed to access the X11 display and open its portion
of the X11 window writes and error message on standard error and
aborts, which is going to take the whole MPI program down.
Assuming that this has not happened, the processes define their color:
my_color = (MPE_Color) (my_rank + 1);
and process of rank zero writes the word "Hello" on the window:
if ( my_rank == 0 )
ierr = MPE_Draw_string( graph, 187, 205, MPE_BLUE, "Hello" );
MPE_Draw_string
takes graph (as returned by the call to MPE_Open_graphics)
as its first argument.
Now each process draws a circle in its own color with the centre at (200,200). This is accomplished by calling
ierr = MPE_Draw_circle( graph, 200, 200, 20+my_rank*5, my_color );
graph as
its first argument.
ierr = MPE_Update( graph );
simply ensures that all that's been drawn on the graph gets
sent to the X11 window that's associated with it.
At this stage the program waits for the user to type something on the console's keyboard. Otherwise the picture would vanish almost instantaneously and we might not have the time enough to admire it:
if ( my_rank == 0 ) {
fprintf( stdout, "Hit any key then return to continue " );
fscanf( stdin, "%s", &ckey );
fprintf( stdout, "\n" );
}
MPI_Barrier( MPI_COMM_WORLD );
Observe that it is process zero that is in charge of the console - this
is always the case in MPICH2 - the other processes wait for it on
the barrier. The barrier must be there, because what follows it is
ierr = MPE_Close_graphics( &graph );
If other processes did not have to wait on the barrier, they would
take their portions of the graph down, but, as it happens, they all
get to it only after the user has typed something on the keyboard.
The program ends with the usual:
MPI_Finalize();
return 0;
}
Now let me introduce officially the synopsis for each of the graphic functions discussed in this section, as well as some other ones to be used in the next section:
int MPE_Open_graphics(MPE_Xgraph *handle, MPI_Comm comm, char *display,
int x, int y, int is_collective);
int MPE_Draw_point(MPE_Xgraph handle, int x, int y, MPE_Color color);
int MPE_Draw_line(MPE_Xgraph handle, int x1, int y1, int x2, int y2,
MPE_Color color);
int MPE_Draw_circle(MPE_Xgraph handle, int centerx, int centery, int radius,
MPE_Color color);
int MPE_Update(MPE_XGraph handle);
int MPE_Num_colors(MPE_Xgraph handle, int *number_of_colors);
int MPE_Make_color_array(MPE_XGraph handle, int number_of_colors,
MPE_Color array[]);
int MPE_Close_graphics(MPE_XGraph handle);
int MPE_Draw_string(MPE_XGraph handle, int x, int y, MPE_Color, char* string);
The predefined colors can be found on
/N/hpc/mpich2/include/mpe_graphics.cIn our case they are
MPE_WHITE, MPE_BLACK, MPE_RED, MPE_YELLOW, MPE_GREEN, MPE_CYAN, MPE_BLUE, MPE_MAGENTA, MPE_AQUAMARINE, MPE_FORESTGREEN, MPE_ORANGE, MPE_MAROON, MPE_BROWN, MPE_PINK, MPE_CORAL, MPE_GRAY
Observe that all processes share the graph. Each process
can access any pixel on the graph. It is up to the
programmer to ensure that they don't step on each other's
toes, unless, of course, this is what the programmer wants.