xref: /petsc/src/ksp/pc/impls/redundant/redundant.c (revision 3e06580049c891b56573b129e10c404d0071d997)
1 #define PETSCKSP_DLL
2 
3 /*
4   This file defines a "solve the problem redundantly on each subgroup of processor" preconditioner.
5 */
6 #include "private/pcimpl.h"     /*I "petscpc.h" I*/
7 #include "petscksp.h"
8 
9 typedef struct {
10   KSP          ksp;
11   PC           pc;                   /* actual preconditioner used on each processor */
12   Vec          xsub,ysub;            /* vectors of a subcommunicator to hold parallel vectors of pc->comm */
13   Vec          xdup,ydup;            /* parallel vector that congregates xsub or ysub facilitating vector scattering */
14   Mat          pmats;                /* matrix and optional preconditioner matrix belong to a subcommunicator */
15   VecScatter   scatterin,scatterout; /* scatter used to move all values to each processor group (subcommunicator) */
16   PetscTruth   useparallelmat;
17   PetscSubcomm psubcomm;
18   PetscInt     nsubcomm;           /* num of data structure PetscSubcomm */
19 } PC_Redundant;
20 
21 #undef __FUNCT__
22 #define __FUNCT__ "PCView_Redundant"
23 static PetscErrorCode PCView_Redundant(PC pc,PetscViewer viewer)
24 {
25   PC_Redundant   *red = (PC_Redundant*)pc->data;
26   PetscErrorCode ierr;
27   PetscMPIInt    rank;
28   PetscTruth     iascii,isstring;
29   PetscViewer    sviewer,subviewer;
30   PetscInt       color = red->psubcomm->color;
31 
32   PetscFunctionBegin;
33   ierr = MPI_Comm_rank(pc->comm,&rank);CHKERRQ(ierr);
34   ierr = PetscTypeCompare((PetscObject)viewer,PETSC_VIEWER_ASCII,&iascii);CHKERRQ(ierr);
35   ierr = PetscTypeCompare((PetscObject)viewer,PETSC_VIEWER_STRING,&isstring);CHKERRQ(ierr);
36   if (iascii) {
37     ierr = PetscViewerASCIIPrintf(viewer,"  Redundant preconditioner: First (color=0) of %D PCs follows\n",red->nsubcomm);CHKERRQ(ierr);
38     ierr = PetscViewerGetSubcomm(viewer,red->pc->comm,&subviewer);CHKERRQ(ierr);
39     if (!color) { /* only view first redundant pc */
40       ierr = PetscViewerASCIIPushTab(viewer);CHKERRQ(ierr);
41       ierr = KSPView(red->ksp,subviewer);CHKERRQ(ierr);
42       ierr = PetscViewerASCIIPopTab(viewer);CHKERRQ(ierr);
43     }
44     ierr = PetscViewerRestoreSubcomm(viewer,red->pc->comm,&subviewer);CHKERRQ(ierr);
45   } else if (isstring) { /* not test it yet! */
46     ierr = PetscViewerStringSPrintf(viewer," Redundant solver preconditioner");CHKERRQ(ierr);
47     ierr = PetscViewerGetSingleton(viewer,&sviewer);CHKERRQ(ierr);
48     if (!rank) {
49       ierr = KSPView(red->ksp,sviewer);CHKERRQ(ierr);
50     }
51     ierr = PetscViewerRestoreSingleton(viewer,&sviewer);CHKERRQ(ierr);
52   } else {
53     SETERRQ1(PETSC_ERR_SUP,"Viewer type %s not supported for PC redundant",((PetscObject)viewer)->type_name);
54   }
55   PetscFunctionReturn(0);
56 }
57 
58 #include "include/private/matimpl.h"        /*I "petscmat.h" I*/
59 #undef __FUNCT__
60 #define __FUNCT__ "PCSetUp_Redundant"
61 static PetscErrorCode PCSetUp_Redundant(PC pc)
62 {
63   PC_Redundant   *red = (PC_Redundant*)pc->data;
64   PetscErrorCode ierr;
65   PetscInt       mstart,mend,mlocal,m,mlocal_sub,rstart_sub,rend_sub,mloc_sub;
66   PetscMPIInt    size;
67   MatReuse       reuse = MAT_INITIAL_MATRIX;
68   MatStructure   str = DIFFERENT_NONZERO_PATTERN;
69   MPI_Comm       comm = pc->comm,subcomm;
70   Vec            vec;
71   PetscMPIInt    subsize,subrank;
72   const char     *prefix;
73 
74   PetscFunctionBegin;
75   ierr = MatGetVecs(pc->pmat,&vec,0);CHKERRQ(ierr);
76   ierr = VecGetSize(vec,&m);CHKERRQ(ierr);
77 
78   if (!pc->setupcalled) {
79     ierr = PetscSubcommCreate(comm,red->nsubcomm,&red->psubcomm);CHKERRQ(ierr);
80     ierr = PetscLogObjectMemory(pc,sizeof(PetscSubcomm));CHKERRQ(ierr);
81 
82     /* create a new PC that processors in each subcomm have copy of */
83     subcomm = red->psubcomm->comm;
84     KSP    subksp;
85     PC     subpc;
86     ierr = KSPCreate(subcomm,&subksp);CHKERRQ(ierr);
87     ierr = PetscLogObjectParent(pc,subksp);CHKERRQ(ierr);
88     ierr = KSPSetType(subksp,KSPPREONLY);CHKERRQ(ierr);
89     ierr = KSPGetPC(subksp,&subpc);CHKERRQ(ierr);
90     ierr = PCSetType(subpc,PCLU);CHKERRQ(ierr);
91     ierr = PCGetOptionsPrefix(pc,&prefix);CHKERRQ(ierr);
92     ierr = KSPSetOptionsPrefix(subksp,prefix);CHKERRQ(ierr);
93     ierr = KSPAppendOptionsPrefix(subksp,"redundant_");CHKERRQ(ierr);
94     ierr = PCSetOptionsPrefix(subpc,prefix);CHKERRQ(ierr);
95     ierr = PCAppendOptionsPrefix(subpc,"redundant_ksp_");CHKERRQ(ierr);
96     red->ksp = subksp;
97     red->pc  = subpc;
98 
99     /* create working vectors xsub/ysub and xdup/ydup */
100     ierr = VecGetLocalSize(vec,&mlocal);CHKERRQ(ierr);
101     ierr = VecGetOwnershipRange(vec,&mstart,&mend);CHKERRQ(ierr);
102 
103     /* get local size of xsub/ysub */
104     ierr = MPI_Comm_size(subcomm,&subsize);CHKERRQ(ierr);
105     ierr = MPI_Comm_rank(subcomm,&subrank);CHKERRQ(ierr);
106     rstart_sub = pc->pmat->rmap.range[red->psubcomm->n*subrank]; /* rstart in xsub/ysub */
107     if (subrank+1 < subsize){
108       rend_sub = pc->pmat->rmap.range[red->psubcomm->n*(subrank+1)];
109     } else {
110       rend_sub = m;
111     }
112     mloc_sub = rend_sub - rstart_sub;
113     ierr = VecCreateMPI(subcomm,mloc_sub,PETSC_DECIDE,&red->ysub);CHKERRQ(ierr);
114     /* create xsub with empty local arrays, because xdup's arrays will be placed into it */
115     ierr = VecCreateMPIWithArray(subcomm,mloc_sub,PETSC_DECIDE,PETSC_NULL,&red->xsub);CHKERRQ(ierr);
116 
117     /* create xdup and ydup. ydup has empty local arrays because ysub's arrays will be place into it.
118        Note: we use communicator dupcomm, not pc->comm! */
119     ierr = VecCreateMPI(red->psubcomm->dupparent,mloc_sub,PETSC_DECIDE,&red->xdup);CHKERRQ(ierr);
120     ierr = VecCreateMPIWithArray(red->psubcomm->dupparent,mloc_sub,PETSC_DECIDE,PETSC_NULL,&red->ydup);CHKERRQ(ierr);
121 
122     /* create vec scatters */
123     if (!red->scatterin){
124       IS       is1,is2;
125       PetscInt *idx1,*idx2,i,j,k;
126 
127       ierr = PetscMalloc(2*red->psubcomm->n*mlocal*sizeof(PetscInt),&idx1);CHKERRQ(ierr);
128       idx2 = idx1 + red->psubcomm->n*mlocal;
129       j = 0;
130       for (k=0; k<red->psubcomm->n; k++){
131         for (i=mstart; i<mend; i++){
132           idx1[j]   = i;
133           idx2[j++] = i + m*k;
134         }
135       }
136       ierr = ISCreateGeneral(comm,red->psubcomm->n*mlocal,idx1,&is1);CHKERRQ(ierr);
137       ierr = ISCreateGeneral(comm,red->psubcomm->n*mlocal,idx2,&is2);CHKERRQ(ierr);
138       ierr = VecScatterCreate(vec,is1,red->xdup,is2,&red->scatterin);CHKERRQ(ierr);
139       ierr = ISDestroy(is1);CHKERRQ(ierr);
140       ierr = ISDestroy(is2);CHKERRQ(ierr);
141 
142       ierr = ISCreateStride(comm,mlocal,mstart+ red->psubcomm->color*m,1,&is1);CHKERRQ(ierr);
143       ierr = ISCreateStride(comm,mlocal,mstart,1,&is2);CHKERRQ(ierr);
144       ierr = VecScatterCreate(red->xdup,is1,vec,is2,&red->scatterout);CHKERRQ(ierr);
145       ierr = ISDestroy(is1);CHKERRQ(ierr);
146       ierr = ISDestroy(is2);CHKERRQ(ierr);
147       ierr = PetscFree(idx1);CHKERRQ(ierr);
148     }
149   }
150   ierr = VecDestroy(vec);CHKERRQ(ierr);
151 
152   /* if pmatrix set by user is sequential then we do not need to gather the parallel matrix */
153   ierr = MPI_Comm_size(comm,&size);CHKERRQ(ierr);
154   if (size == 1) {
155     red->useparallelmat = PETSC_FALSE;
156   }
157 
158   if (red->useparallelmat) {
159     if (pc->setupcalled == 1 && pc->flag == DIFFERENT_NONZERO_PATTERN) {
160       /* destroy old matrices */
161       if (red->pmats) {
162         ierr = MatDestroy(red->pmats);CHKERRQ(ierr);
163       }
164     } else if (pc->setupcalled == 1) {
165       reuse = MAT_REUSE_MATRIX;
166       str   = SAME_NONZERO_PATTERN;
167     }
168 
169     /* grab the parallel matrix and put it into processors of a subcomminicator */
170     /*--------------------------------------------------------------------------*/
171     ierr = VecGetLocalSize(red->ysub,&mlocal_sub);CHKERRQ(ierr);
172     ierr = MatGetRedundantMatrix(pc->pmat,red->psubcomm->n,red->psubcomm->comm,mlocal_sub,reuse,&red->pmats);CHKERRQ(ierr);
173 
174     /* tell PC of the subcommunicator its operators */
175     ierr = PCSetOperators(red->pc,red->pmats,red->pmats,str);CHKERRQ(ierr);
176   } else {
177     ierr = PCSetOperators(red->pc,pc->mat,pc->pmat,pc->flag);CHKERRQ(ierr);
178   }
179   if (pc->setfromoptionscalled){
180     ierr = KSPSetFromOptions(red->ksp);CHKERRQ(ierr);
181   }
182   ierr = KSPSetUp(red->ksp);CHKERRQ(ierr);
183   PetscFunctionReturn(0);
184 }
185 
186 #undef __FUNCT__
187 #define __FUNCT__ "PCApply_Redundant"
188 static PetscErrorCode PCApply_Redundant(PC pc,Vec x,Vec y)
189 {
190   PC_Redundant   *red = (PC_Redundant*)pc->data;
191   PetscErrorCode ierr;
192   PetscScalar    *array;
193 
194   PetscFunctionBegin;
195   /* scatter x to xdup */
196   ierr = VecScatterBegin(x,red->xdup,INSERT_VALUES,SCATTER_FORWARD,red->scatterin);CHKERRQ(ierr);
197   ierr = VecScatterEnd(x,red->xdup,INSERT_VALUES,SCATTER_FORWARD,red->scatterin);CHKERRQ(ierr);
198 
199   /* place xdup's local array into xsub */
200   ierr = VecGetArray(red->xdup,&array);CHKERRQ(ierr);
201   ierr = VecPlaceArray(red->xsub,(const PetscScalar*)array);CHKERRQ(ierr);
202 
203   /* apply preconditioner on each processor */
204   ierr = PCApply(red->pc,red->xsub,red->ysub);CHKERRQ(ierr);
205   ierr = VecResetArray(red->xsub);CHKERRQ(ierr);
206   ierr = VecRestoreArray(red->xdup,&array);CHKERRQ(ierr);
207 
208   /* place ysub's local array into ydup */
209   ierr = VecGetArray(red->ysub,&array);CHKERRQ(ierr);
210   ierr = VecPlaceArray(red->ydup,(const PetscScalar*)array);CHKERRQ(ierr);
211 
212   /* scatter ydup to y */
213   ierr = VecScatterBegin(red->ydup,y,INSERT_VALUES,SCATTER_FORWARD,red->scatterout);CHKERRQ(ierr);
214   ierr = VecScatterEnd(red->ydup,y,INSERT_VALUES,SCATTER_FORWARD,red->scatterout);CHKERRQ(ierr);
215   ierr = VecResetArray(red->ydup);CHKERRQ(ierr);
216   ierr = VecRestoreArray(red->ysub,&array);CHKERRQ(ierr);
217   PetscFunctionReturn(0);
218 }
219 
220 #undef __FUNCT__
221 #define __FUNCT__ "PCDestroy_Redundant"
222 static PetscErrorCode PCDestroy_Redundant(PC pc)
223 {
224   PC_Redundant   *red = (PC_Redundant*)pc->data;
225   PetscErrorCode ierr;
226 
227   PetscFunctionBegin;
228   if (red->scatterin)  {ierr = VecScatterDestroy(red->scatterin);CHKERRQ(ierr);}
229   if (red->scatterout) {ierr = VecScatterDestroy(red->scatterout);CHKERRQ(ierr);}
230   if (red->ysub)       {ierr = VecDestroy(red->ysub);CHKERRQ(ierr);}
231   if (red->xsub)       {ierr = VecDestroy(red->xsub);CHKERRQ(ierr);}
232   if (red->xdup)       {ierr = VecDestroy(red->xdup);CHKERRQ(ierr);}
233   if (red->ydup)       {ierr = VecDestroy(red->ydup);CHKERRQ(ierr);}
234   if (red->pmats) {
235     ierr = MatDestroy(red->pmats);CHKERRQ(ierr);
236   }
237   if (red->psubcomm) {ierr = PetscSubcommDestroy(red->psubcomm);CHKERRQ(ierr);}
238   if (red->ksp) {ierr = KSPDestroy(red->ksp);CHKERRQ(ierr);}
239   ierr = PetscFree(red);CHKERRQ(ierr);
240   PetscFunctionReturn(0);
241 }
242 
243 #undef __FUNCT__
244 #define __FUNCT__ "PCSetFromOptions_Redundant"
245 static PetscErrorCode PCSetFromOptions_Redundant(PC pc)
246 {
247   PetscErrorCode ierr;
248   PC_Redundant   *red = (PC_Redundant*)pc->data;
249 
250   PetscFunctionBegin;
251   ierr = PetscOptionsHead("Redundant options");CHKERRQ(ierr);
252   ierr = PetscOptionsInt("-pc_redundant_number","Number of redundant pc","PCRedundantSetNumber",red->nsubcomm,&red->nsubcomm,0);CHKERRQ(ierr);
253   ierr = PetscOptionsTail();CHKERRQ(ierr);
254   PetscFunctionReturn(0);
255 }
256 
257 EXTERN_C_BEGIN
258 #undef __FUNCT__
259 #define __FUNCT__ "PCRedundantSetNumber_Redundant"
260 PetscErrorCode PETSCKSP_DLLEXPORT PCRedundantSetNumber_Redundant(PC pc,PetscInt nreds)
261 {
262   PC_Redundant *red = (PC_Redundant*)pc->data;
263 
264   PetscFunctionBegin;
265   red->nsubcomm = nreds;
266   PetscFunctionReturn(0);
267 }
268 EXTERN_C_END
269 
270 #undef __FUNCT__
271 #define __FUNCT__ "PCRedundantSetNumber"
272 /*@
273    PCRedundantSetNumber - Sets the number of redundant preconditioner contexts.
274 
275    Collective on PC
276 
277    Input Parameters:
278 +  pc - the preconditioner context
279 -  nredundant - number of redundant preconditioner contexts; for example if you are using 64 MPI processes and
280                               use an nredundant of 4 there will be 4 parallel solves each on 16 = 64/4 processes.
281 
282    Level: advanced
283 
284 .keywords: PC, redundant solve
285 @*/
286 PetscErrorCode PETSCKSP_DLLEXPORT PCRedundantSetNumber(PC pc,PetscInt nredundant)
287 {
288   PetscErrorCode ierr,(*f)(PC,PetscInt);
289 
290   PetscFunctionBegin;
291   PetscValidHeaderSpecific(pc,PC_COOKIE,1);
292   if (nredundant <= 0) SETERRQ1(PETSC_ERR_ARG_WRONG, "num of redundant pc %D must be positive",nredundant);
293   ierr = PetscObjectQueryFunction((PetscObject)pc,"PCRedundantSetNumber_C",(void (**)(void))&f);CHKERRQ(ierr);
294   if (f) {
295     ierr = (*f)(pc,nredundant);CHKERRQ(ierr);
296   }
297   PetscFunctionReturn(0);
298 }
299 
300 EXTERN_C_BEGIN
301 #undef __FUNCT__
302 #define __FUNCT__ "PCRedundantSetScatter_Redundant"
303 PetscErrorCode PETSCKSP_DLLEXPORT PCRedundantSetScatter_Redundant(PC pc,VecScatter in,VecScatter out)
304 {
305   PC_Redundant   *red = (PC_Redundant*)pc->data;
306   PetscErrorCode ierr;
307 
308   PetscFunctionBegin;
309   ierr = PetscObjectReference((PetscObject)in);CHKERRQ(ierr);
310   if (red->scatterin) { ierr = VecScatterDestroy(red->scatterin);CHKERRQ(ierr); }
311   red->scatterin  = in;
312   ierr = PetscObjectReference((PetscObject)out);CHKERRQ(ierr);
313   if (red->scatterout) { ierr = VecScatterDestroy(red->scatterout);CHKERRQ(ierr); }
314   red->scatterout = out;
315   PetscFunctionReturn(0);
316 }
317 EXTERN_C_END
318 
319 #undef __FUNCT__
320 #define __FUNCT__ "PCRedundantSetScatter"
321 /*@
322    PCRedundantSetScatter - Sets the scatter used to copy values into the
323      redundant local solve and the scatter to move them back into the global
324      vector.
325 
326    Collective on PC
327 
328    Input Parameters:
329 +  pc - the preconditioner context
330 .  in - the scatter to move the values in
331 -  out - the scatter to move them out
332 
333    Level: advanced
334 
335 .keywords: PC, redundant solve
336 @*/
337 PetscErrorCode PETSCKSP_DLLEXPORT PCRedundantSetScatter(PC pc,VecScatter in,VecScatter out)
338 {
339   PetscErrorCode ierr,(*f)(PC,VecScatter,VecScatter);
340 
341   PetscFunctionBegin;
342   PetscValidHeaderSpecific(pc,PC_COOKIE,1);
343   PetscValidHeaderSpecific(in,VEC_SCATTER_COOKIE,2);
344   PetscValidHeaderSpecific(out,VEC_SCATTER_COOKIE,3);
345   ierr = PetscObjectQueryFunction((PetscObject)pc,"PCRedundantSetScatter_C",(void (**)(void))&f);CHKERRQ(ierr);
346   if (f) {
347     ierr = (*f)(pc,in,out);CHKERRQ(ierr);
348   }
349   PetscFunctionReturn(0);
350 }
351 
352 EXTERN_C_BEGIN
353 #undef __FUNCT__
354 #define __FUNCT__ "PCRedundantGetPC_Redundant"
355 PetscErrorCode PETSCKSP_DLLEXPORT PCRedundantGetPC_Redundant(PC pc,PC *innerpc)
356 {
357   PC_Redundant *red = (PC_Redundant*)pc->data;
358 
359   PetscFunctionBegin;
360   *innerpc = red->pc;
361   PetscFunctionReturn(0);
362 }
363 EXTERN_C_END
364 
365 #undef __FUNCT__
366 #define __FUNCT__ "PCRedundantGetPC"
367 /*@
368    PCRedundantGetPC - Gets the sequential PC created by the redundant PC.
369 
370    Not Collective
371 
372    Input Parameter:
373 .  pc - the preconditioner context
374 
375    Output Parameter:
376 .  innerpc - the sequential PC
377 
378    Level: advanced
379 
380 .keywords: PC, redundant solve
381 @*/
382 PetscErrorCode PETSCKSP_DLLEXPORT PCRedundantGetPC(PC pc,PC *innerpc)
383 {
384   PetscErrorCode ierr,(*f)(PC,PC*);
385 
386   PetscFunctionBegin;
387   PetscValidHeaderSpecific(pc,PC_COOKIE,1);
388   PetscValidPointer(innerpc,2);
389   ierr = PetscObjectQueryFunction((PetscObject)pc,"PCRedundantGetPC_C",(void (**)(void))&f);CHKERRQ(ierr);
390   if (f) {
391     ierr = (*f)(pc,innerpc);CHKERRQ(ierr);
392   }
393   PetscFunctionReturn(0);
394 }
395 
396 EXTERN_C_BEGIN
397 #undef __FUNCT__
398 #define __FUNCT__ "PCRedundantGetOperators_Redundant"
399 PetscErrorCode PETSCKSP_DLLEXPORT PCRedundantGetOperators_Redundant(PC pc,Mat *mat,Mat *pmat)
400 {
401   PC_Redundant *red = (PC_Redundant*)pc->data;
402 
403   PetscFunctionBegin;
404   if (mat)  *mat  = red->pmats;
405   if (pmat) *pmat = red->pmats;
406   PetscFunctionReturn(0);
407 }
408 EXTERN_C_END
409 
410 #undef __FUNCT__
411 #define __FUNCT__ "PCRedundantGetOperators"
412 /*@
413    PCRedundantGetOperators - gets the sequential matrix and preconditioner matrix
414 
415    Not Collective
416 
417    Input Parameter:
418 .  pc - the preconditioner context
419 
420    Output Parameters:
421 +  mat - the matrix
422 -  pmat - the (possibly different) preconditioner matrix
423 
424    Level: advanced
425 
426 .keywords: PC, redundant solve
427 @*/
428 PetscErrorCode PETSCKSP_DLLEXPORT PCRedundantGetOperators(PC pc,Mat *mat,Mat *pmat)
429 {
430   PetscErrorCode ierr,(*f)(PC,Mat*,Mat*);
431 
432   PetscFunctionBegin;
433   PetscValidHeaderSpecific(pc,PC_COOKIE,1);
434   if (mat)  PetscValidPointer(mat,2);
435   if (pmat) PetscValidPointer(pmat,3);
436   ierr = PetscObjectQueryFunction((PetscObject)pc,"PCRedundantGetOperators_C",(void (**)(void))&f);CHKERRQ(ierr);
437   if (f) {
438     ierr = (*f)(pc,mat,pmat);CHKERRQ(ierr);
439   }
440   PetscFunctionReturn(0);
441 }
442 
443 /* -------------------------------------------------------------------------------------*/
444 /*MC
445      PCREDUNDANT - Runs a preconditioner for the entire problem on subgroups of processors
446 
447      Options for the redundant preconditioners can be set with -redundant_pc_xxx
448 
449   Options Database:
450 .  -pc_redundant_number <n> - number of redundant solves, for example if you are using 64 MPI processes and
451                               use an n of 4 there will be 4 parallel solves each on 16 = 64/4 processes.
452 
453    Level: intermediate
454 
455 .seealso:  PCCreate(), PCSetType(), PCType (for list of available types), PCRedundantSetScatter(),
456            PCRedundantGetPC(), PCRedundantGetOperators(), PCRedundantSetNumber()
457 M*/
458 
459 EXTERN_C_BEGIN
460 #undef __FUNCT__
461 #define __FUNCT__ "PCCreate_Redundant"
462 PetscErrorCode PETSCKSP_DLLEXPORT PCCreate_Redundant(PC pc)
463 {
464   PetscErrorCode ierr;
465   PC_Redundant   *red;
466   PetscMPIInt    size;
467 
468   PetscFunctionBegin;
469   ierr = PetscNew(PC_Redundant,&red);CHKERRQ(ierr);
470   ierr = PetscLogObjectMemory(pc,sizeof(PC_Redundant));CHKERRQ(ierr);
471   ierr = MPI_Comm_size(pc->comm,&size);CHKERRQ(ierr);
472   red->nsubcomm       = size;
473   red->useparallelmat = PETSC_TRUE;
474   pc->data            = (void*)red;
475 
476   pc->ops->apply           = PCApply_Redundant;
477   pc->ops->applytranspose  = 0;
478   pc->ops->setup           = PCSetUp_Redundant;
479   pc->ops->destroy         = PCDestroy_Redundant;
480   pc->ops->setfromoptions  = PCSetFromOptions_Redundant;
481   pc->ops->view            = PCView_Redundant;
482   ierr = PetscObjectComposeFunctionDynamic((PetscObject)pc,"PCRedundantSetScatter_C","PCRedundantSetScatter_Redundant",
483                     PCRedundantSetScatter_Redundant);CHKERRQ(ierr);
484   ierr = PetscObjectComposeFunctionDynamic((PetscObject)pc,"PCRedundantSetNumber_C","PCRedundantSetNumber_Redundant",
485                     PCRedundantSetNumber_Redundant);CHKERRQ(ierr);
486   ierr = PetscObjectComposeFunctionDynamic((PetscObject)pc,"PCRedundantGetPC_C","PCRedundantGetPC_Redundant",
487                     PCRedundantGetPC_Redundant);CHKERRQ(ierr);
488   ierr = PetscObjectComposeFunctionDynamic((PetscObject)pc,"PCRedundantGetOperators_C","PCRedundantGetOperators_Redundant",
489                     PCRedundantGetOperators_Redundant);CHKERRQ(ierr);
490   PetscFunctionReturn(0);
491 }
492 EXTERN_C_END
493