source: branches/source_numpy_conversion/pypar-numeric/contrib/mpi.c @ 7248

Last change on this file since 7248 was 5779, checked in by steve, 16 years ago

Added the old version of pypar which works with Numeric. Necessary for parallel code until we move anuga to numpy (and then we can use pypar as distribute via sourceforge).

File size: 20.7 KB
Line 
1/************************************************************************/
2/* PyPAR - Parallel Python using MPI                                    */
3/* Copyright (C) 2001, 2002 Ole M. Nielsen, Gian Paolo Ciceri           */
4/*                                                                      */
5/* See enclosed README file for details of installation and use.        */
6/*                                                                      */   
7/* This program is free software; you can redistribute it and/or modify */
8/* it under the terms of the GNU General Public License as published by */
9/* the Free Software Foundation; either version 2 of the License, or    */
10/* (at your option) any later version.                                  */
11/*                                                                      */     
12/* This program is distributed in the hope that it will be useful,      */
13/* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
14/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
15/* GNU General Public License (http://www.gnu.org/copyleft/gpl.html)    */
16/* for more details.                                                    */
17/*                                                                      */
18/* You should have received a copy of the GNU General Public License    */
19/* along with this program; if not, write to the Free Software          */
20/*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307*/
21/*                                                                      */
22/*                                                                      */
23/* Contact addresses: Ole.Nielsen@anu.edu.au, gp.ciceri@acm.org         */
24/*                                                                      */
25/* version 1.2.1, 16 February 2002                                      */
26/*   Status block, MPI_ANY_TAG, MPI_ANY_SOURCE exported                 */
27/* Version 1.2, 15 February 2002                                        */   
28/*   Scatter added by Gian Paolo Ciceri                                 */
29/* Version 1.1, 14 February 2002                                        */   
30/*   Bcast added by Gian Paolo Ciceri                                   */
31/* Version 1.0.2, 10 February 2002                                      */
32/*   Modified by Gian Paulo Ciceri to allow pypar run under Python 2.2  */
33/* Version 1.0.1, 8 February 2002                                       */
34/*   Modified to install on SUN enterprise systems under Mpich          */
35/* Version 1.0, 7 February 2002                                         */
36/*   First public release for Python 2.1 (OMN)                          */
37/************************************************************************/
38
39
40#include "Python.h"
41#include "mpi.h"
42#include "Numeric/arrayobject.h"
43
44// to handle MPI constants export (shamelessly stolen from _cursesmodule.c)
45#define SetDictInt(string,ch) \
46        PyDict_SetItemString(ModDict, string, PyInt_FromLong((long) (ch)));
47
48// kludge to remap struct MPI_op to int (easier to handle by python)
49#define mpi_MAX 1
50#define mpi_MIN 2
51#define mpi_SUM 3
52#define mpi_PROD 4
53#define mpi_LAND 5
54#define mpi_BAND 6
55#define mpi_LOR 7
56#define mpi_BOR 8
57#define mpi_LXOR 9
58#define mpi_BXOR 10
59#define mpi_MAXLOC 11
60#define mpi_MINLOC 12
61#define mpi_REPLACE 13
62
63
64
65MPI_Datatype type_map(PyArrayObject *x) { 
66
67  //
68  // TYPE    py_type  mpi_type  bytes  symbol
69  // ----------------------------------------
70  // INT       4        6         4      'i'
71  // LONG      5        8         8      'l'
72  // FLOAT     6       10         4      'f' 
73  // DOUBLE    7       11         8      'd'
74 
75 
76  int py_type;
77  MPI_Datatype mpi_type;
78 
79  if (x -> nd != 1) {
80    PyErr_SetString(PyExc_ValueError, "Array must be 1 dimensional");
81    return (MPI_Datatype) 0;
82  }     
83     
84  py_type = x -> descr -> type_num;     
85  if (py_type == PyArray_DOUBLE) 
86    mpi_type = MPI_DOUBLE;
87  else if (py_type == PyArray_LONG)   
88    mpi_type = MPI_LONG; 
89  else if (py_type == PyArray_FLOAT) 
90    mpi_type = MPI_FLOAT;
91  else if (py_type == PyArray_INT) 
92    mpi_type = MPI_INT;
93  else {
94    PyErr_SetString(PyExc_ValueError, "Array must be of type int or float");
95    return 0;
96  }     
97
98  //printf("Types: %d %d.\n", py_type, mpi_type);
99 
100  return mpi_type;
101}   
102
103MPI_Op op_map(int py_op) { 
104 
105  MPI_Op mpi_op;
106 
107  if (py_op == mpi_MAX) 
108    mpi_op = MPI_MAX;
109  else if (py_op == mpi_MIN)   
110    mpi_op = MPI_MIN; 
111  else if (py_op == mpi_SUM)   
112    mpi_op = MPI_SUM; 
113  else if (py_op == mpi_PROD)   
114    mpi_op = MPI_PROD; 
115  else if (py_op == mpi_LAND)   
116    mpi_op = MPI_LAND; 
117  else if (py_op == mpi_BAND)   
118    mpi_op = MPI_BAND; 
119  else if (py_op == mpi_LOR)   
120    mpi_op = MPI_LOR; 
121  else if (py_op == mpi_BOR)   
122    mpi_op = MPI_BOR; 
123  else if (py_op == mpi_LXOR)   
124    mpi_op = MPI_LXOR; 
125  else if (py_op == mpi_BXOR)   
126    mpi_op = MPI_BXOR; 
127  else if (py_op == mpi_MAXLOC)
128    mpi_op = MPI_MAXLOC;
129  else if (py_op == mpi_MINLOC)   
130    mpi_op = MPI_MINLOC; 
131  else if (py_op == mpi_REPLACE)   
132    mpi_op = MPI_REPLACE; 
133  else {
134    PyErr_SetString(PyExc_ValueError, "Operation unknown");
135    return 0;
136  }     
137
138  //printf("Op: %d.\n", py_op);
139 
140  return mpi_op;
141} 
142
143/*********************************************************/
144/* send_string                                           */
145/* Send string of characters                             */
146/*                                                       */
147/*********************************************************/
148static PyObject *send_string(PyObject *self, PyObject *args) {
149  char *s;
150  int destination, tag, length, err;
151 
152  /* process the parameters */
153  if (!PyArg_ParseTuple(args, "s#ii", &s, &length, &destination, &tag))
154    return NULL;
155 
156  /* call the MPI routine */
157  err = MPI_Send(s, length, MPI_CHAR, destination, tag, MPI_COMM_WORLD);
158
159  return Py_BuildValue("i", err);
160}
161
162/**********************************************************/
163/* receive_string                                         */
164/* Receive string of characters                           */
165/*                                                        */
166/**********************************************************/
167static PyObject *receive_string(PyObject *self, PyObject *args) {
168  char *s;
169  int source, tag, length, err, st_length; 
170  MPI_Status status;
171
172  /* process the parameters */
173  if (!PyArg_ParseTuple(args, "s#ii", &s, &length, &source, &tag))
174    return NULL;
175   
176  /* call the MPI routine */
177  err = MPI_Recv(s, length, MPI_CHAR, source, tag, MPI_COMM_WORLD, &status);
178   
179  MPI_Get_count(&status, MPI_CHAR, &st_length); 
180  // status.st_length is not available in all MPI implementations
181  //Alternative is: MPI_Get_elements(MPI_Status *, MPI_Datatype, int *);
182
183  return Py_BuildValue("i(iiii)", err, status.MPI_SOURCE, status.MPI_TAG,
184  status.MPI_ERROR, st_length); 
185}
186
187/**********************************************************/
188/* bcast_string                                           */
189/* Broadcast string of characters                         */
190/*                                                        */
191/**********************************************************/
192static PyObject *bcast_string(PyObject *self, PyObject *args) {
193  char *s;
194  int source, length, err; 
195
196  /* process the parameters */
197  if (!PyArg_ParseTuple(args, "s#i", &s, &length, &source))
198    return NULL;
199   
200  /* call the MPI routine */
201  err = MPI_Bcast(s, length, MPI_CHAR, source, MPI_COMM_WORLD);
202   
203  return Py_BuildValue("i", err); 
204}
205
206/**********************************************************/
207/* scatter_string                                         */
208/* Scatter string of characters                           */
209/*                                                        */
210/**********************************************************/
211static PyObject *scatter_string(PyObject *self, PyObject *args) {
212  char *s;
213  char *d;
214  int source, length, err; 
215
216  /* process the parameters */
217  if (!PyArg_ParseTuple(args, "sisi", &s, &length, &d, &source))
218    return NULL;
219   
220  /* call the MPI routine */
221  err = MPI_Scatter(s, length, MPI_CHAR, d, length,  MPI_CHAR, source, MPI_COMM_WORLD);
222   
223  return Py_BuildValue("i", err); 
224}
225
226/**********************************************************/
227/* gather_string                                         */
228/* Gather string of characters                           */
229/*                                                        */
230/**********************************************************/
231static PyObject *gather_string(PyObject *self, PyObject *args) {
232  char *s;
233  char *d;
234  int source, length, err; 
235
236  /* process the parameters */
237  if (!PyArg_ParseTuple(args, "sisi", &s, &length, &d, &source))
238    return NULL;
239   
240  /* call the MPI routine */
241  err = MPI_Gather(s, length, MPI_CHAR, d, length,  MPI_CHAR, source, MPI_COMM_WORLD);
242   
243  return Py_BuildValue("i", err); 
244}
245
246
247/**********************************************************/
248/* send_array                                             */
249/* Send Numeric array of type float, double, int, or long */
250/*                                                        */
251/**********************************************************/
252static PyObject *send_array(PyObject *self, PyObject *args) {
253  PyObject *input;
254  PyArrayObject *x;
255  int destination, tag, err;
256  MPI_Datatype mpi_type;
257 
258  /* process the parameters */
259  if (!PyArg_ParseTuple(args, "Oii", &input, &destination, &tag))
260    return NULL;
261   
262  /* Make Numeric array from general sequence type (no cost if already Numeric)*/   
263  x = (PyArrayObject *)
264    PyArray_ContiguousFromObject(input, PyArray_NOTYPE, 0, 0);
265   
266  /* Input check and determination of MPI type */         
267  mpi_type = type_map(x);
268  if (!mpi_type) return NULL;
269   
270  /* call the MPI routine */
271  err = MPI_Send(x->data, x->dimensions[0], mpi_type, destination, tag,\
272           MPI_COMM_WORLD);
273           
274  Py_DECREF(x);           
275           
276  return Py_BuildValue("i", err);
277}
278
279/*************************************************************/
280/* receive_array                                             */
281/* Receive Numeric array of type float, double, int, or long */
282/*                                                           */
283/*************************************************************/
284static PyObject *receive_array(PyObject *self, PyObject *args) {
285  PyArrayObject *x;
286  int source, tag, err, st_length;
287  MPI_Datatype mpi_type;
288  MPI_Status status;
289
290  /* process the parameters */
291  if (!PyArg_ParseTuple(args, "Oii", &x, &source, &tag))
292    return NULL;
293
294  /* Input check and determination of MPI type */         
295  mpi_type = type_map(x);
296  if (!mpi_type) return NULL; 
297     
298  /* call the MPI routine */
299  err =  MPI_Recv(x->data, x->dimensions[0], mpi_type, source, tag, \
300         MPI_COMM_WORLD, &status);
301         
302  MPI_Get_count(&status, mpi_type, &st_length); 
303  // status.st_length is not available in all MPI implementations
304  //Alternative is: MPI_Get_elements(MPI_Status *, MPI_Datatype, int *);
305         
306     
307  return Py_BuildValue("i(iiii)", err, status.MPI_SOURCE, status.MPI_TAG,
308  status.MPI_ERROR, st_length); 
309}
310
311
312/*************************************************************/
313/* bcast_array                                               */
314/* Broadcast Num.  array of type float, double, int, or long */
315/*                                                           */
316/*************************************************************/
317static PyObject *bcast_array(PyObject *self, PyObject *args) {
318  PyArrayObject *x;
319  int source, err;
320  MPI_Datatype mpi_type;
321  MPI_Status status;
322
323  /* process the parameters */
324  if (!PyArg_ParseTuple(args, "Oi", &x, &source))
325    return NULL;
326
327  /* Input check and determination of MPI type */         
328  mpi_type = type_map(x);
329  if (!mpi_type) return NULL; 
330     
331  /* call the MPI routine */
332  err =  MPI_Bcast(x->data, x->dimensions[0], mpi_type, source, \
333         MPI_COMM_WORLD);
334     
335  return Py_BuildValue("i", err);
336}
337
338/*************************************************************/
339/* scatter_array                                             */
340/* Scatter Num.    array of type float, double, int, or long */
341/*                                                           */
342/*************************************************************/
343static PyObject *scatter_array(PyObject *self, PyObject *args) {
344  PyArrayObject *x;
345  PyArrayObject *d;
346  int length, source, err;
347  MPI_Datatype mpi_type;
348  MPI_Status status;
349
350  /* process the parameters */
351  if (!PyArg_ParseTuple(args, "OiOi", &x, &length, &d, &source))
352    return NULL;
353
354  /* Input check and determination of MPI type */         
355  mpi_type = type_map(x);
356  if (!mpi_type) return NULL; 
357     
358  /* call the MPI routine */
359  err =  MPI_Scatter(x->data, length, mpi_type, d->data, length, mpi_type, source, \
360         MPI_COMM_WORLD);
361     
362  return Py_BuildValue("i", err);
363}
364
365
366/*************************************************************/
367/* gather_array                                              */
368/* Gather Num.     array of type float, double, int, or long */
369/*                                                           */
370/*************************************************************/
371static PyObject *gather_array(PyObject *self, PyObject *args) {
372  PyArrayObject *x;
373  PyArrayObject *d;
374  int length, source, err;
375  MPI_Datatype mpi_type;
376  MPI_Status status;
377
378  /* process the parameters */
379  if (!PyArg_ParseTuple(args, "OiOi", &x, &length, &d, &source))
380    return NULL;
381
382  /* Input check and determination of MPI type */         
383  mpi_type = type_map(x);
384  if (!mpi_type) return NULL; 
385     
386  /* call the MPI routine */
387  err =  MPI_Gather(x->data, length, mpi_type, d->data, length, mpi_type, source, \
388         MPI_COMM_WORLD);
389     
390  return Py_BuildValue("i", err);
391}
392
393
394/*************************************************************/
395/* reduce_array                                              */
396/* Reduce Num.     array of type float, double, int, or long */
397/*                                                           */
398/*************************************************************/
399static PyObject *reduce_array(PyObject *self, PyObject *args) {
400  PyArrayObject *x;
401  PyArrayObject *d;
402  int length, source, op, err;
403  MPI_Datatype mpi_type;
404  MPI_Status status;
405  MPI_Op mpi_op;
406
407  /* process the parameters */
408  if (!PyArg_ParseTuple(args, "OOiii", &x, &d, &length, &op, &source))
409    return NULL;
410   
411  /* Input check and determination of MPI type */         
412  mpi_type = type_map(x);
413  if (!mpi_type) return NULL; 
414 
415  /* Input check and determination of MPI op */ 
416  //printf("op: %d\n", op);         
417  mpi_op = op_map(op);
418  if (!mpi_op) return NULL; 
419   
420         
421  if (op == mpi_MAXLOC || op == mpi_MINLOC) {
422    //not implemented
423    return Py_BuildValue("i", -666);
424  }
425  else {
426  /* call the MPI routine */
427  err =  MPI_Reduce(x->data, d->data, length, mpi_type, mpi_op, source, \
428         MPI_COMM_WORLD);
429  }
430         
431     
432  return Py_BuildValue("i", err);
433}
434
435
436
437/*********************************************************/
438/* MPI calls rank, size, finalize, abort                 */
439/*                                                       */
440/*********************************************************/
441
442static PyObject * rank(PyObject *self, PyObject *args) {
443  int myid;
444
445  MPI_Comm_rank(MPI_COMM_WORLD,&myid);
446  return Py_BuildValue("i", myid);
447}
448
449static PyObject * size(PyObject *self, PyObject *args) {
450  int numprocs; 
451
452  MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
453  return Py_BuildValue("i", numprocs);
454}
455 
456static PyObject * Get_processor_name(PyObject *self, PyObject *args) { 
457  char processor_name[MPI_MAX_PROCESSOR_NAME];
458  int  namelen;
459
460  MPI_Get_processor_name(processor_name,&namelen);
461  return Py_BuildValue("s#", processor_name, namelen);
462}   
463 
464static PyObject * Finalize(PyObject *self, PyObject *args) { 
465  int error;
466
467  error = MPI_Finalize();
468  return Py_BuildValue("i", error); 
469} 
470
471static PyObject * Abort(PyObject *self, PyObject *args) { 
472  int error, code=0;
473 
474  error = MPI_Abort(MPI_COMM_WORLD, code);
475  return Py_BuildValue("i", error); 
476} 
477
478static PyObject * Barrier(PyObject *self, PyObject *args) { 
479  int error;
480 
481  error = MPI_Barrier(MPI_COMM_WORLD);
482  return Py_BuildValue("i", error);   
483}   
484
485static PyObject * Wtime(PyObject *self, PyObject *args) {     
486  double t;
487 
488  t = MPI_Wtime();
489  return Py_BuildValue("d", t);     
490}     
491 
492/**********************************/
493/* Method table for python module */
494/**********************************/
495
496static struct PyMethodDef MethodTable[] = {
497  {"size", size, METH_VARARGS}, 
498  {"rank", rank, METH_VARARGS}, 
499  {"Barrier", Barrier, METH_VARARGS},         
500  {"Wtime", Wtime, METH_VARARGS},           
501  {"Get_processor_name", Get_processor_name, METH_VARARGS},             
502  {"Finalize", Finalize, METH_VARARGS},       
503  {"Abort", Abort, METH_VARARGS},         
504  {"send_string", send_string, METH_VARARGS},
505  {"receive_string", receive_string, METH_VARARGS},     
506  {"bcast_string", bcast_string, METH_VARARGS},       
507  {"scatter_string", scatter_string, METH_VARARGS},       
508  {"gather_string", gather_string, METH_VARARGS},       
509  {"send_array", send_array, METH_VARARGS},
510  {"receive_array", receive_array, METH_VARARGS},   
511  {"bcast_array", bcast_array, METH_VARARGS},             
512  {"scatter_array", scatter_array, METH_VARARGS},             
513  {"gather_array", gather_array, METH_VARARGS},             
514  {"reduce_array", reduce_array, METH_VARARGS},             
515  {NULL, NULL}
516};
517
518
519/***************************/
520/* Initialisation Function */
521/***************************/
522
523void initmpi(){
524  int error, argc = 0; //Dummy
525  char **argv;         //Dummy
526  PyObject *m, *ModDict;
527
528  //printf("Initialising MPI\n");
529  error = MPI_Init(&argc, &argv); 
530  //printf("MPI Initialised\n"); 
531 
532  //FIXME: Make a sensible errorcheck here
533 
534  m = Py_InitModule("mpi", MethodTable);
535 
536  // to handle MPI symbolic constants
537  ModDict = PyModule_GetDict(m); 
538  SetDictInt("MPI_ANY_TAG", MPI_ANY_TAG);
539  SetDictInt("MPI_ANY_SOURCE", MPI_ANY_SOURCE);
540  SetDictInt("mpi_MAX", mpi_MAX);
541  SetDictInt("mpi_MIN", mpi_MIN);
542  SetDictInt("mpi_SUM", mpi_SUM);
543  SetDictInt("mpi_PROD", mpi_PROD);
544  SetDictInt("mpi_LAND", mpi_LAND);
545  SetDictInt("mpi_BAND", mpi_BAND);
546  SetDictInt("mpi_LOR", mpi_LOR);
547  SetDictInt("mpi_BOR", mpi_BOR);
548  SetDictInt("mpi_LXOR", mpi_LXOR);
549  SetDictInt("mpi_BXOR", mpi_BXOR);
550  SetDictInt("mpi_MAXLOC", mpi_MAXLOC);
551  SetDictInt("mpi_MINLOC", mpi_MINLOC);
552  SetDictInt("mpi_REPLACE", mpi_REPLACE);
553
554  //SetDictInt("MPI_COMM_WORLD", MPI_COMM_WORLD); 
555   
556  import_array();     //Necessary for handling of NumPY structures 
557}
558
559 
560 
561 
562 
563/*********************************************************************/
564/* OBSOLETE STUFF                                                    */
565/*********************************************************************/
566
567/******************************************/
568/* keep for doc of Numeric arrays etc     */ 
569/******************************************/
570
571void print_real_array(PyArrayObject *x) { 
572  int i;
573  for (i=0; i<x->dimensions[0]; i++) {
574    printf("%f ", *(double*) (x->data + i*x->strides[0]));
575  }
576}
577
578void print_int_array(PyArrayObject *x) { 
579  int i;
580  for (i=0; i<x->dimensions[0]; i++) {
581    printf("%d ", *(int*) (x->data + i*x->strides[0]));
582  }
583}
584
585
586/*********************************************************/
587/* send_real_array                                       */
588/* Send Numeric array of double floating point numbers   */
589/*                                                       */
590/*********************************************************/
591static PyObject *send_real_array(PyObject *self, PyObject *args) {
592  PyObject *input;
593  PyArrayObject *x;
594  int destination, tag, err;
595 
596  /* process the parameters */
597  if (!PyArg_ParseTuple(args, "Oii", &input, &destination, &tag))
598    return NULL;
599
600  /* Make Numeric array from general sequence type (no cost if already Numeric)*/   
601  x = (PyArrayObject *)
602    PyArray_ContiguousFromObject(input, PyArray_DOUBLE, 0, 0);
603
604  /* call the MPI routine */
605  err = MPI_Send(x->data, x->dimensions[0], MPI_DOUBLE, destination, tag,\
606           MPI_COMM_WORLD);
607
608  Py_DECREF(x);                           
609  return Py_BuildValue("i", err);
610}
611
612/**********************************************************/
613/* receive_real_array                                     */
614/* Receive Numeric array of double floating point numbers */
615/*                                                        */
616/**********************************************************/
617static PyObject *receive_real_array(PyObject *self, PyObject *args) {
618  PyArrayObject *x;
619  int source, tag, err;
620  MPI_Status status;
621
622  /* process the parameters */
623  if (!PyArg_ParseTuple(args, "Oii", &x, &source, &tag))
624    return NULL;
625 
626  /* call the MPI routine */
627  err =  MPI_Recv(x->data, x->dimensions[0], MPI_DOUBLE, source, tag, \
628         MPI_COMM_WORLD, &status);
629
630
631  return Py_BuildValue("i", err);
632}
633 
634
635
Note: See TracBrowser for help on using the repository browser.