1 /*
2  * ***************************************************************************
3  * MALOC = < Minimal Abstraction Layer for Object-oriented C >
4  * Copyright (C) 1994-- Michael Holst
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  * rcsid="$Id: vcom.c,v 1.25 2010/08/12 05:40:21 fetk Exp $"
21  * ***************************************************************************
22  */
23 
24 /*
25  * ***************************************************************************
26  * File:     vcom.c
27  *
28  * Purpose:  Class Vcom: methods.
29  *
30  * Author:   Nathan Baker and Michael Holst
31  * ***************************************************************************
32  */
33 
34 #include "vcom_p.h"
35 
36 VEMBED(rcsid="$Id: vcom.c,v 1.25 2010/08/12 05:40:21 fetk Exp $")
37 
38 /*
39  * ***************************************************************************
40  * Class Vcom: Inlineable methods
41  * ***************************************************************************
42  */
43 #if !defined(VINLINE_MALOC)
44 
45 #endif /* if !defined(VINLINE_MALOC) */
46 
47 /*
48  * ***************************************************************************
49  * Class Vcom: Non-inlineable methods
50  * ***************************************************************************
51  */
52 
53 /*
54  * ***************************************************************************
55  * Routine:  Vcom_init
56  *
57  * Purpose:  The Vmp initializer.
58  *
59  * Author:   Michael Holst
60  * ***************************************************************************
61  */
Vcom_init(int * argc,char *** argv)62 VPUBLIC int Vcom_init(int *argc, char ***argv)
63 {
64 #if defined(HAVE_MPI_H)
65     return (MPI_SUCCESS == MPI_Init(argc,argv));
66 #else
67     return 1;
68 #endif
69 }
70 
71 /*
72  * ***************************************************************************
73  * Routine:  Vcom_finalize
74  *
75  * Purpose:   The Vmp finalizerr.
76  *
77  * Author:   Michael Holst
78  * ***************************************************************************
79  */
Vcom_finalize(void)80 VPUBLIC int Vcom_finalize(void)
81 {
82 #if defined(HAVE_MPI_H)
83     return (MPI_SUCCESS == MPI_Finalize());
84 #else
85     return 1;
86 #endif
87 }
88 
89 /*
90  * ***************************************************************************
91  * Routine:  Vcom_ctor
92  *
93  * Purpose:  Construct the communications object
94  *
95  * Notes:    This routine sets up data members of class and initializes MPI.
96  *
97  * Author:   Nathan Baker and Michael Holst
98  * ***************************************************************************
99  */
Vcom_ctor(int commtype)100 VPUBLIC Vcom* Vcom_ctor(int commtype)
101 {
102     int rc;
103     Vcom *thee = VNULL;
104 
105     /* Set up the structure */
106     thee       = Vmem_malloc( VNULL, 1, sizeof(Vcom) );
107     thee->core = Vmem_malloc( VNULL, 1, sizeof(Vcom_core) );
108 
109     /* Call the real constructor */
110     rc = Vcom_ctor2(thee, commtype);
111 
112     /* Destroy the guy if something went wrong */
113     if (rc == 0) {
114         Vmem_free( VNULL, 1, sizeof(Vcom_core), (void**)&(thee->core) );
115         Vmem_free( VNULL, 1, sizeof(Vcom), (void**)&thee );
116     }
117 
118     return thee;
119 }
120 
121 /*
122  * ***************************************************************************
123  * Routine:  Vcom_ctor2
124  *
125  * Purpose:  Construct the communications object
126  *
127  * Notes:    This routine sets up data members of class and initializes MPI.
128  *
129  *           This is broken into two parts to be callable from FORTRAN.
130  *
131  * Author:   Nathan Baker and Michael Holst
132  * ***************************************************************************
133  */
Vcom_ctor2(Vcom * thee,int commtype)134 VPUBLIC int Vcom_ctor2(Vcom *thee, int commtype)
135 {
136     int rc = 0;
137 
138 #if defined(HAVE_MPI_H)
139     char estr[MPI_MAX_ERROR_STRING];
140     int elen, dummy;
141     Vcom_core *core = thee->core;
142 #endif
143 
144     /*
145      * See what type of communications we should use.  Maybe each type of
146      * communications object should have its own ctor2() function.
147      */
148     switch ( commtype ) {
149       case 1:             /* MPI 1.1 */
150         thee->type = commtype;
151 
152 #if defined(HAVE_MPI_H)
153         /* Start up MPI */
154         rc = MPI_Initialized(&dummy);
155         if (rc != MPI_SUCCESS) {
156             MPI_Error_string(rc, estr, &elen);
157             Vnm_print(2, "Vcom_ctor2: MPI_Init returned error: %s\n",
158               estr);
159             return 0;
160         }
161 
162         /* Get the total number of processors */
163         rc = MPI_Comm_size(MPI_COMM_WORLD, &(thee->mpi_size));
164         if (rc != MPI_SUCCESS) {
165             MPI_Error_string(rc, estr, &elen);
166             Vnm_print(2, "Vcom_ctor2: MPI_Comm_size returned error: %s\n",
167               estr);
168             return 0;
169         }
170 
171         /* Get my processor rank */
172         rc = MPI_Comm_rank(MPI_COMM_WORLD, &(thee->mpi_rank));
173         if (rc != MPI_SUCCESS) {
174             MPI_Error_string(rc, estr, &elen);
175             Vnm_print(2, "Vcom_ctor2: MPI_Comm_rank returned error: %s\n",
176               estr);
177             return 0;
178         }
179 
180         /* Construct the communications group including all processors */
181         core->mpi_comm = MPI_COMM_WORLD;
182 
183         /* Initialize Vnm with MPI rank */
184         Vnm_setIoTag(thee->mpi_rank, thee->mpi_size);
185 
186         /* Some i/o */
187         Vnm_print(2,"Vcom_ctor2: process %d of %d is ALIVE!\n",
188             thee->mpi_rank, thee->mpi_size);
189 
190         rc = 1;
191         break;
192 
193 #else  /* defined(HAVE_MPI_H) */
194 
195         /* this might not be an error if this is a sequential code... */
196         rc = 1;
197         break;
198 
199 #endif /* defined(HAVE_MPI_H) */
200 
201       default:
202         Vnm_print(2, "Vcom_ctor2: Invalid communications type!\n");
203         rc = 0;
204 
205     } /* switch (commtype) */
206 
207     return rc;
208 }
209 
210 /*
211  * ***************************************************************************
212  * Routine:  Vcom_resize
213  *
214  * Purpose:  Resize (shrink) the communications group to include only newsize
215  *           number of processors
216  *
217  * Notes:    Obsolete processes are given rank of -1 and size of 0
218  *
219  * Returns:  1 if sucessful
220  *
221  * Author:   Nathan Baker
222  * ***************************************************************************
223  */
Vcom_resize(Vcom * thee,int newsize)224 VPUBLIC int Vcom_resize(Vcom *thee, int newsize)
225 {
226 #if defined(HAVE_MPI_H)
227     int color;
228     MPI_Comm oldcomm;
229     Vcom_core *core = thee->core;
230 #endif
231 
232     switch (thee->type) {
233         case 1:  /* MPI 1.1 */
234 #if defined(HAVE_MPI_H)
235             /* This is a no-op for obsolete processes */
236             if (core->mpi_comm == MPI_COMM_NULL) return 1;
237             Vcom_barr(thee);
238             /* Split the communications group.  We will ignore all processes
239              * with rank outside the desired size */
240             if (newsize > thee->mpi_size) {
241                 Vnm_print(2, "Vcom_resize:  Requested number of processors (%d) greater than original size (%d)!\n", newsize, thee->mpi_size);
242                 return 0;
243             }
244             if (thee->mpi_rank < newsize) color = 0;
245             else color = MPI_UNDEFINED;
246             MPI_Comm_dup(core->mpi_comm, &oldcomm);
247             if (MPI_Comm_split(oldcomm, color, 0, &(core->mpi_comm))
248               != MPI_SUCCESS) {
249                 Vnm_print(2, "Vcom_resize:  Failed to split communicator!\n");
250                 return 0;
251             }
252             MPI_Comm_free(&oldcomm);
253             if (core->mpi_comm != MPI_COMM_NULL) {
254                 MPI_Comm_rank(core->mpi_comm, &(thee->mpi_rank));
255                 MPI_Comm_size(core->mpi_comm, &(thee->mpi_size));
256             } else {
257                 thee->mpi_rank = -1;
258                 thee->mpi_size = 0;
259             }
260             Vnm_print(0, "Vcom_resize: New comm size = %d\n", thee->mpi_size);
261             return 1;
262 #else
263             Vnm_print(2, "Vcom_resize: Not compiled with MPI!\n");
264             return 0;
265 #endif
266             break;
267         default:
268             Vnm_print(2, "Vcom_resize: Invalid communications type!\n");
269             return 0;
270     }
271 }
272 
273 /*
274  * ***************************************************************************
275  * Routine:  Vcom_dtor
276  *
277  * Purpose:  Destroy the communications object
278  *
279  * Author:   Nathan Baker and Michael Holst
280  * ***************************************************************************
281  */
Vcom_dtor(Vcom ** thee)282 VPUBLIC void Vcom_dtor(Vcom **thee)
283 {
284     if ((*thee) != VNULL) {
285         Vcom_dtor2(*thee);
286         Vmem_free( VNULL, 1, sizeof(Vcom_core), (void**)&((*thee)->core) );
287         Vmem_free( VNULL, 1, sizeof(Vcom), (void**)thee );
288     }
289 }
290 
291 /*
292  * ***************************************************************************
293  * Routine:  Vcom_dtor2
294  *
295  * Purpose:  Destroy the communications object
296  *
297  * Notes:    This is broken into two parts to be callable from FORTRAN.
298  *
299  * Author:   Nathan Baker and Michael Holst
300  * ***************************************************************************
301  */
Vcom_dtor2(Vcom * thee)302 VPUBLIC void Vcom_dtor2(Vcom *thee)
303 {
304 #if defined(HAVE_MPI_H)
305     int err;
306     Vcom_core *core = thee->core;
307 #endif
308 
309     /*
310      * Do various things depending on what type of communications object
311      * this is.  Maybe each object type should have its own dtor2() function.
312      */
313     switch (thee->type) {
314 
315       case 1:  /* MPI 1.1 */
316 #if defined(HAVE_MPI_H)
317         /* Destroy the communicator */
318         if ((core->mpi_comm != MPI_COMM_NULL) &&
319             (core->mpi_comm != MPI_COMM_WORLD)) {
320             Vnm_print(0, "Vcom_dtor2:  Freeing MPI communicator...\n");
321             MPI_Comm_free(&(core->mpi_comm));
322         }
323 
324 #if 0
325         err = MPI_Finalize();
326         if (err != MPI_SUCCESS) {
327             Vnm_print(2, "Vcom_dtor2: MPI_Finalize returned %d\n", err);
328         }
329 #endif
330 #endif
331       default:
332         return;
333     }
334 }
335 
336 /*
337  * ***************************************************************************
338  * Routine:  Vcom_send
339  *
340  * Purpose:  Send a buffer.  Returns 1 on success.
341  *
342  * Args:     des   = rank of receiving processor
343  *           buf   = buffer containing message
344  *           len   = number of items (of declared type) in buffer
345  *           type  = type of items in message
346  *                   0 => MPI_BYTE
347  *                   1 => MPI_INT
348  *                   2 => MPI_DOUBLE
349  *                   3 => MPI_CHAR
350  *           block = toggles blocking on (=1) and off (=0)
351  *
352  * Returns:  1 if successful
353  *
354  * Author:   Nathan Baker and Michael Holst
355  * ***************************************************************************
356  */
Vcom_send(Vcom * thee,int des,void * buf,int len,int type,int block)357 VPUBLIC int Vcom_send(Vcom *thee, int des, void *buf, int len, int type,
358     int block)
359 {
360 #if defined(HAVE_MPI_H)
361     int tag = VCOM_MPI_TAG;  /* MPI tag */
362     MPI_Datatype datatype;
363     Vcom_core *core = thee->core;
364 #endif
365 
366     int err = 1;             /* Error flag (success = 1) */
367 
368     /* Bail if we've received any errors */
369     VASSERT(thee != VNULL);
370 
371     if (thee->error != 0) {
372         Vnm_print(2, "Vcom_send:  Have non-zero error state (%d)!\n",
373           thee->error);
374         return 0;
375     }
376 
377     /* Figure out data type to use */
378 #if defined(HAVE_MPI_H)
379     switch(type) {
380         case 0:
381             datatype = MPI_BYTE;
382             break;
383         case 1:
384             datatype = MPI_INT;
385             break;
386         case 2:
387             datatype = MPI_DOUBLE;
388             break;
389         case 3:
390             datatype = MPI_CHAR;
391             break;
392         default:
393             Vnm_print(2, "Vcom_send: Bogus datatype (%d), bailing!\n", type);
394             return 0;
395     }
396 #endif
397 
398     /* Send routine depends on comm object type */
399     switch(thee->type) {
400 
401       case 1: /* MPI 1.1 */
402 #if defined(HAVE_MPI_H)
403         if (core->mpi_comm == MPI_COMM_NULL) return 1;
404         /* To block or not to block... */
405         if (block == 1) {
406             err = MPI_Send(buf, len, datatype, des, tag, core->mpi_comm);
407             err = (MPI_SUCCESS == err);
408         } else { /* if (block == 1) */
409             err = MPI_Isend(buf, len, datatype, des, tag, core->mpi_comm,
410                   &(core->mpi_request));
411             err = (MPI_SUCCESS == err);
412         } /* if (block == 1) */
413 #else
414         Vnm_print(2, "Vcom_send: Vcom not compiled with MPI!\n");
415         return 0;
416 #endif
417         break;
418       default:
419         Vnm_print(2, "Vcom_send: Invalid communications type!\n");
420         return 0;
421     }
422 
423     return err;
424 }
425 
426 /*
427  * ***************************************************************************
428  * Routine:  Vcom_recv
429  *
430  * Purpose:  Receive a (character) buffer.  Returns 1 on success.
431  *
432  * Args:     src   = rank of sending processor
433  *           buf   = pointer to buffer of previously allocated memory
434  *           len   = number of items (of declared type) in buffer
435  *           type  = type of items in message
436  *                   0 => MPI_BYTE
437  *                   1 => MPI_INT
438  *                   2 => MPI_DOUBLE
439  *                   3 => MPI_CHAR
440  *           block = toggles blocking on (=1) and off (=0)
441  *
442  * Returns:  1 if successful
443  *
444  * Notes:    The blocking flag is present, but not used.  All receives are
445  *           assumed to be blocking.  A non-blocking receive would be *very*
446  *           ugly to implement (signals or something?).
447  *
448  * Author:   Nathan Baker and Michael Holst
449  * ***************************************************************************
450  */
Vcom_recv(Vcom * thee,int src,void * buf,int len,int type,int block)451 VPUBLIC int Vcom_recv(Vcom *thee, int src, void *buf, int len, int type,
452     int block)
453 {
454     int err = 0;             /* Error flag (success = 1) */
455 
456 #if defined(HAVE_MPI_H)
457     int tag = VCOM_MPI_TAG;  /* MPI tag */
458     MPI_Datatype datatype;
459     Vcom_core *core = thee->core;
460 #endif
461 
462     /* Bail if we've received any errors */
463     VASSERT(thee != VNULL);
464     if (thee->error != 0) {
465         Vnm_print(2, "Vcom_send:  Have non-zero error state (%d)!\n",
466           thee->error);
467         return 0;
468     }
469 
470 
471 #if defined(HAVE_MPI_H)
472     switch(type) {
473         case 0:
474             datatype = MPI_BYTE;
475             break;
476         case 1:
477             datatype = MPI_INT;
478             break;
479         case 2:
480             datatype = MPI_DOUBLE;
481             break;
482         case 3:
483             datatype = MPI_CHAR;
484             break;
485         default:
486             Vnm_print(2, "Vcom_recv: Bogus datatype (%d), bailing!\n", type);
487             return 0;
488     }
489 #endif
490 
491     /* Send routine depends on comm object type */
492     switch(thee->type) {
493 
494       case 1: /* MPI 1.1 */
495 #if defined(HAVE_MPI_H)
496         if (core->mpi_comm == MPI_COMM_NULL) return 1;
497         /* To block or not to block... */
498         if (block == 1) {
499             err = MPI_Recv(buf, len, datatype, src, tag,
500                   core->mpi_comm, &(core->mpi_status));
501             err = (MPI_SUCCESS == err);
502         } else {
503             Vnm_print(2, "Vcom_recv: WARNING! Non-blocking receive not implemented!\n");
504             return 0;
505         }
506 #else
507         Vnm_print(2, "Vcom_recv: Vcom not compiled with MPI!\n");
508         return 0;
509 #endif
510 
511         break;
512       default:
513         Vnm_print(2, "Vcom_recv: Invalid communications type!\n");
514         return 0;
515     }
516     return err;
517 }
518 
519 /*
520  * ***************************************************************************
521  * Routine:  Vcom_size
522  *
523  * Purpose:  Get the number of PEs in communicator
524  *
525  * Returns:  Number of PEs or -1 if error
526  *
527  * Author:   Nathan Baker and Michael Holst
528  * ***************************************************************************
529  */
Vcom_size(Vcom * thee)530 VPUBLIC int Vcom_size(Vcom *thee)
531 {
532 #if defined(HAVE_MPI_H)
533     Vcom_core *core = thee->core;
534 #endif
535 
536     VASSERT(thee != VNULL);
537 
538     if ( thee->type == 1) {
539 #if defined(HAVE_MPI_H)
540         if (core->mpi_comm == MPI_COMM_NULL) return 0;
541         return thee->mpi_size;
542 #else
543         return 1;
544 #endif
545     } else { return -1; }
546 }
547 
548 /*
549  * ***************************************************************************
550  * Routine:  Vcom_rank
551  *
552  * Purpose:  Get the ID of the local PE
553  *
554  * Returns:  PE rank or -1 if error
555  *
556  * Author:   Nathan Baker and Michael Holst
557  * ***************************************************************************
558  */
Vcom_rank(Vcom * thee)559 VPUBLIC int Vcom_rank(Vcom *thee)
560 {
561 #if defined(HAVE_MPI_H)
562     Vcom_core *core = thee->core;
563 #endif
564 
565     VASSERT(thee != VNULL);
566 
567     if ( thee->type == 1) {
568 #if defined(HAVE_MPI_H)
569         if (core->mpi_comm == MPI_COMM_NULL) return -1;
570         return thee->mpi_rank;
571 #else
572         return 0;
573 #endif
574     } else { return -1; }
575 }
576 
577 /*
578  * ***************************************************************************
579  * Routine:  Vcom_barr
580  *
581  * Purpose:  Synchronization barrier.
582  *
583  * Returns:  1 if succesful
584  *
585  * Author:   Michael Holst
586  * ***************************************************************************
587  */
Vcom_barr(Vcom * thee)588 VPUBLIC int Vcom_barr(Vcom *thee)
589 {
590 #if defined(HAVE_MPI_H)
591     int err;
592     Vcom_core *core = thee->core;
593 #endif
594 
595     VASSERT(thee != VNULL);
596 
597     if ( thee->type == 1) {
598 
599 #if defined(HAVE_MPI_H)
600         if (core->mpi_comm != MPI_COMM_NULL) {
601             err = MPI_Barrier(core->mpi_comm);
602             return (err == MPI_SUCCESS);
603         } else return 1;
604 #else
605         Vnm_print(2, "Vcom_barr: Vcom not compiled with MPI!\n");
606         return 0;
607 #endif
608     } else {
609         Vnm_print(2, "Vcom_barr: Invalid communications type!\n");
610         return 0;
611     }
612     return 0;
613 }
614 
615 /*
616  * ***************************************************************************
617  * Routine:  Vcom_getCount
618  *
619  * Purpose:  Perform a blocking probe to get the length (in number of items of
620  *           specified type) of an incoming message and place it in the
621  *           argument ``length".
622  *
623  *           type  = type of items in message
624  *                   0 => MPI_BYTE
625  *                   1 => MPI_INT
626  *                   2 => MPI_DOUBLE
627  *                   3 => MPI_CHAR
628  *
629  * Author:   Nathan Baker
630  * ***************************************************************************
631  */
Vcom_getCount(Vcom * thee,int src,int * length,int type)632 VPUBLIC int Vcom_getCount(Vcom *thee, int src, int *length, int type)
633 {
634 #if defined(HAVE_MPI_H)
635     MPI_Datatype datatype;
636     Vcom_core *core = thee->core;
637 #endif
638 
639     VASSERT(thee != VNULL);
640 
641 #if defined(HAVE_MPI_H)
642     switch(type) {
643         case 0:
644             datatype = MPI_BYTE;
645             break;
646         case 1:
647             datatype = MPI_INT;
648             break;
649         case 2:
650             datatype = MPI_DOUBLE;
651             break;
652         case 3:
653             datatype = MPI_CHAR;
654             break;
655         default:
656             Vnm_print(2,"Vcom_getCount: Bogus datatype (%d), bailing!\n",type);
657             return 0;
658     }
659 #endif
660 
661     if ( thee->type == 1) {
662 #if defined(HAVE_MPI_H)
663         if (core->mpi_comm == MPI_COMM_NULL) return 1;
664         MPI_Probe(src, VCOM_MPI_TAG, core->mpi_comm, &(core->mpi_status));
665         return MPI_Get_count(&(core->mpi_status), datatype, length);
666 #else
667         Vnm_print(2, "Vcom_getCount: Vcom not compiled with MPI!\n");
668         return -1;
669 #endif
670     } else { return -1; }
671 }
672 
673 /*
674  * ***************************************************************************
675  * Routine:  Vcom_reduce
676  *
677  * Purpose:  Perform a reduction of the data across all processors.  This is
678  *           equivalent (and in the case of MPI is identical to) MPI_Allreduce.
679  *           Basically, the specified operations are appleed to each member of
680  *           the sendbuf across all processors and the results are written to
681  *           recvbuf.
682  *
683  * Args:     sendbuf = buffer containing `length` items of the specified type
684  *                     to be operated on
685  *           recvbuf = buffer containing `length` items of the specified type
686  *                     after operation
687  *           length = number of items
688  *           type  = type of items in message
689  *                   0 => MPI_BYTE
690  *                   1 => MPI_INT
691  *                   2 => MPI_DOUBLE
692  *                   3 => MPI_CHAR
693  *           op = operation to perform
694  *                0 => MPI_SUM
695  *                1 => MPI_PROD
696  *                2 => MPI_MIN
697  *                3 => MPI_MAX
698  *
699  * Author:   Nathan Baker
700  * ***************************************************************************
701  */
Vcom_reduce(Vcom * thee,void * sendbuf,void * recvbuf,int length,int type,int op)702 VPUBLIC int Vcom_reduce(Vcom *thee, void *sendbuf, void *recvbuf, int length,
703     int type, int op)
704 {
705     int memsize;
706 
707 #if defined(HAVE_MPI_H)
708     MPI_Datatype datatype;
709     MPI_Op optype;
710     Vcom_core *core = thee->core;
711 #endif
712 
713     VASSERT(thee != VNULL);
714 
715 #if defined(HAVE_MPI_H)
716     switch(type) {
717         case 0:
718             datatype = MPI_BYTE;
719             break;
720         case 1:
721             datatype = MPI_INT;
722             break;
723         case 2:
724             datatype = MPI_DOUBLE;
725             break;
726         case 3:
727             datatype = MPI_CHAR;
728             break;
729         default:
730             Vnm_print(2, "Vcom_recv: Bogus datatype (%d), bailing!\n", type);
731             return 0;
732     }
733     switch(op) {
734         case 0:
735             optype = MPI_SUM;
736             break;
737         case 1:
738             optype = MPI_PROD;
739             break;
740         case 2:
741             optype = MPI_MIN;
742             break;
743         case 3:
744             optype = MPI_MAX;
745             break;
746         default:
747             Vnm_print(2, "Vcom_reduce: Bogus optype (%d), bailing!\n", type);
748             return 0;
749     }
750 #endif
751 
752     if ( thee->type == 1) {
753 
754 #if defined(HAVE_MPI_H)
755 
756         if (core->mpi_comm == MPI_COMM_NULL) return 1;
757         return MPI_Allreduce(sendbuf, recvbuf, length, datatype, optype,
758           core->mpi_comm);
759 
760 #else
761         Vnm_print(0, "Vcom_reduce:  Not compiled with MPI, doing simple copy.\n");
762         switch(type) {
763             case 0:
764                 memsize = 1;
765                 break;
766             case 1:
767                 memsize = sizeof(int);
768                 break;
769             case 2:
770                 memsize = sizeof(double);
771                 break;
772             case 3:
773                 memsize = sizeof(char);
774                 break;
775             default:
776                 Vnm_print(2, "Vcom_recv: Bogus datatype (%d), bailing!\n", type);
777                 return 0;
778         }
779 
780         memcpy(recvbuf, sendbuf, memsize*length);
781         return 1;
782 
783 #endif
784 
785     } else { return -1; }
786 }
787 
788