xref: /petsc/src/ksp/pc/impls/hmg/hmg.c (revision 2475b7ca256cea2a4b7cbf2d8babcda14e5fa36e)
1 #include <petscdm.h>
2 #include <petscctable.h>
3 #include <petsc/private/matimpl.h>
4 #include <petsc/private/pcmgimpl.h>
5 #include <petsc/private/pcimpl.h>      /*I "petscpc.h" I*/
6 
7 typedef struct {
8   PC               innerpc;            /* A MG inner PC (Hypre or PCGAMG) to setup interpolations and coarse operators  */
9   char*            innerpctype;        /* PCGAMG or PCHYPRE */
10   PetscBool        reuseinterp;        /* A flag indicates if or not to reuse the interpolations */
11   PetscBool        subcoarsening;
12 } PC_HMG;
13 
14 PetscErrorCode PCSetFromOptions_HMG(PetscOptionItems*,PC);
15 PetscErrorCode PCReset_MG(PC);
16 
17 static PetscErrorCode PCHMGExtractSubMatrix_Private(Mat pmat,Mat *submat,MatReuse reuse,PetscInt blocksize)
18 {
19   IS             isrow;
20   PetscErrorCode ierr;
21   PetscInt       rstart,rend;
22   MPI_Comm       comm;
23 
24   PetscFunctionBegin;
25   ierr = PetscObjectGetComm((PetscObject)pmat,&comm);CHKERRQ(ierr);
26   ierr = MatGetOwnershipRange(pmat,&rstart,&rend);CHKERRQ(ierr);
27   if ((rend-rstart)%blocksize != 0) SETERRQ3(comm,PETSC_ERR_ARG_INCOMP,"Block size %d is inconsisent for [%d, %d) \n",blocksize,rstart,rend);
28   ierr = ISCreateStride(comm,(rend-rstart)/blocksize,rstart,blocksize,&isrow);
29   ierr = MatCreateSubMatrix(pmat,isrow,isrow,reuse,submat);CHKERRQ(ierr);
30   ierr = ISDestroy(&isrow);CHKERRQ(ierr);
31   PetscFunctionReturn(0);
32 }
33 
34 static PetscErrorCode PCHMGExpandInterpolation_Private(Mat subinterp, Mat *interp, PetscInt blocksize)
35 {
36   PetscInt              subrstart,subrend,subrowsize,subcolsize,subcstart,subcend,rowsize,colsize;
37   PetscInt              subrow,row,nz,*d_nnz,*o_nnz,i,j,dnz,onz,max_nz,*indices;
38   const PetscInt        *idx;
39   const PetscScalar     *values;
40   PetscErrorCode        ierr;
41   MPI_Comm              comm;
42 
43   PetscFunctionBegin;
44   ierr = PetscObjectGetComm((PetscObject)subinterp,&comm);CHKERRQ(ierr);
45   ierr = MatGetOwnershipRange(subinterp,&subrstart,&subrend);CHKERRQ(ierr);
46   subrowsize = subrend-subrstart;
47   rowsize = subrowsize*blocksize;
48   ierr = PetscCalloc2(rowsize,&d_nnz,rowsize,&o_nnz);CHKERRQ(ierr);
49   ierr = MatGetOwnershipRangeColumn(subinterp,&subcstart,&subcend);CHKERRQ(ierr);
50   subcolsize = subcend - subcstart;
51   colsize    = subcolsize*blocksize;
52   max_nz = 0;
53   for (subrow=subrstart;subrow<subrend;subrow++) {
54     ierr = MatGetRow(subinterp,subrow,&nz,&idx,NULL);CHKERRQ(ierr);
55     if (max_nz<nz) max_nz = nz;
56     dnz = 0; onz = 0;
57     for (i=0;i<nz;i++) {
58       if(idx[i]>=subcstart && idx[i]<subcend) dnz++;
59       else onz++;
60     }
61     for (i=0;i<blocksize;i++) {
62       d_nnz[(subrow-subrstart)*blocksize+i] = dnz;
63       o_nnz[(subrow-subrstart)*blocksize+i] = onz;
64     }
65     ierr = MatRestoreRow(subinterp,subrow,&nz,&idx,NULL);CHKERRQ(ierr);
66   }
67   ierr = MatCreateAIJ(comm,rowsize,colsize,PETSC_DETERMINE,PETSC_DETERMINE,0,d_nnz,0,o_nnz,interp);CHKERRQ(ierr);
68   ierr = MatSetOption(*interp,MAT_IGNORE_OFF_PROC_ENTRIES,PETSC_TRUE);CHKERRQ(ierr);
69   ierr = MatSetOption(*interp,MAT_IGNORE_ZERO_ENTRIES,PETSC_TRUE);CHKERRQ(ierr);
70   ierr = MatSetOption(*interp,MAT_NEW_NONZERO_ALLOCATION_ERR,PETSC_TRUE);CHKERRQ(ierr);
71   ierr = MatSetFromOptions(*interp);CHKERRQ(ierr);
72 
73   ierr = MatSetUp(*interp);CHKERRQ(ierr);
74   ierr = PetscFree2(d_nnz,o_nnz);CHKERRQ(ierr);
75   ierr = PetscMalloc1(max_nz,&indices);CHKERRQ(ierr);
76   for (subrow=subrstart; subrow<subrend; subrow++) {
77     ierr = MatGetRow(subinterp,subrow,&nz,&idx,&values);CHKERRQ(ierr);
78     for (i=0;i<blocksize;i++) {
79       row = subrow*blocksize+i;
80       for (j=0;j<nz;j++) {
81         indices[j] = idx[j]*blocksize+i;
82       }
83       ierr = MatSetValues(*interp,1,&row,nz,indices,values,INSERT_VALUES);CHKERRQ(ierr);
84     }
85     ierr = MatRestoreRow(subinterp,subrow,&nz,&idx,&values);CHKERRQ(ierr);
86   }
87   ierr = PetscFree(indices);CHKERRQ(ierr);
88   ierr = MatAssemblyBegin(*interp,MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
89   ierr = MatAssemblyEnd(*interp,MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
90   PetscFunctionReturn(0);
91 }
92 
93 PetscErrorCode PCSetUp_HMG(PC pc)
94 {
95   PetscErrorCode     ierr;
96   Mat                PA, submat;
97   PC_MG              *mg   = (PC_MG*)pc->data;
98   PC_HMG             *hmg   = (PC_HMG*) mg->innerctx;
99   MPI_Comm           comm;
100   PetscInt           level;
101   PetscInt           num_levels;
102   Mat                *operators,*interpolations;
103   PetscInt           blocksize;
104   const char         *prefix;
105   PCMGGalerkinType   galerkin;
106 
107   PetscFunctionBegin;
108   ierr = PetscObjectGetComm((PetscObject)pc,&comm);CHKERRQ(ierr);
109   if (pc->setupcalled) {
110    if (hmg->reuseinterp) {
111      /* If we did not use Galerkin in the last call or we have a different sparsity pattern now,
112       * we have to build from scratch
113       * */
114      ierr = PCMGGetGalerkin(pc,&galerkin);CHKERRQ(ierr);
115      if (galerkin == PC_MG_GALERKIN_NONE || pc->flag != SAME_NONZERO_PATTERN) pc->setupcalled = PETSC_FALSE;
116      ierr = PCMGSetGalerkin(pc,PC_MG_GALERKIN_PMAT);CHKERRQ(ierr);
117      ierr = PCSetUp_MG(pc);CHKERRQ(ierr);
118      PetscFunctionReturn(0);
119     } else {
120      ierr = PCReset_MG(pc);CHKERRQ(ierr);
121      pc->setupcalled = PETSC_FALSE;
122     }
123   }
124 
125   /* Create an inner PC (GAMG or HYPRE) */
126   if (!hmg->innerpc) {
127     ierr = PCCreate(comm,&hmg->innerpc);CHKERRQ(ierr);
128     /* If users do not set an inner pc type, we need to set a default value */
129     if (!hmg->innerpctype) {
130     /* If hypre is available, use hypre, otherwise, use gamg */
131 #if PETSC_HAVE_HYPRE
132       ierr = PetscStrallocpy(PCHYPRE,&(hmg->innerpctype));CHKERRQ(ierr);
133 #else
134       ierr = PetscStrallocpy(PCGAMG,&(hmg->innerpctype));CHKERRQ(ierr);
135 #endif
136     }
137     ierr = PCSetType(hmg->innerpc,hmg->innerpctype);CHKERRQ(ierr);
138   }
139   ierr = PCGetOperators(pc,NULL,&PA);CHKERRQ(ierr);
140   /* Users need to correctly set a block size of matrix in order to use subspace coarsening */
141   ierr = MatGetBlockSize(PA,&blocksize);CHKERRQ(ierr);
142   if (blocksize<=1) hmg->subcoarsening = PETSC_FALSE;
143   /* Extract a submatrix for constructing subinterpolations */
144   if (hmg->subcoarsening) {
145     ierr = PCHMGExtractSubMatrix_Private(PA,&submat,MAT_INITIAL_MATRIX,blocksize);CHKERRQ(ierr);
146     PA = submat;
147   }
148   ierr = PCSetOperators(hmg->innerpc,PA,PA);CHKERRQ(ierr);
149   if (hmg->subcoarsening) {
150    ierr = MatDestroy(&PA);CHKERRQ(ierr);
151   }
152   /* Setup inner PC correctly. During this step, matrix will be coarsened */
153   ierr = PCSetUseAmat(hmg->innerpc,PETSC_FALSE);CHKERRQ(ierr);
154   ierr = PetscObjectGetOptionsPrefix((PetscObject)pc,&prefix);CHKERRQ(ierr);
155   ierr = PetscObjectSetOptionsPrefix((PetscObject)hmg->innerpc,prefix);CHKERRQ(ierr);
156   ierr = PetscObjectAppendOptionsPrefix((PetscObject)hmg->innerpc,"hmg_inner_");CHKERRQ(ierr);
157   ierr = PCSetFromOptions(hmg->innerpc);CHKERRQ(ierr);
158   ierr = PCSetUp(hmg->innerpc);
159 
160   /* Obtain interpolations IN PLACE. For BoomerAMG, (I,J,data) is reused to avoid memory overhead */
161   ierr = PCGetInterpolations(hmg->innerpc,&num_levels,&interpolations);CHKERRQ(ierr);
162   /* We can reuse the coarse operators when we do the full space coarsening */
163   if (!hmg->subcoarsening) {
164     ierr = PCGetCoarseOperators(hmg->innerpc,&num_levels,&operators);CHKERRQ(ierr);
165   }
166 
167   ierr = PCDestroy(&hmg->innerpc);CHKERRQ(ierr);
168   hmg->innerpc = 0;
169   ierr = PCMGSetLevels_MG(pc,num_levels,NULL);CHKERRQ(ierr);
170   /* Set coarse matrices and interpolations to PCMG */
171   for (level=num_levels-1; level>0; level--) {
172     Mat P=0, pmat=0;
173     Vec b, x,r;
174     if (hmg->subcoarsening) {
175       /* Grow interpolation. In the future, we should use MAIJ */
176       ierr = PCHMGExpandInterpolation_Private(interpolations[level-1],&P,blocksize);CHKERRQ(ierr);
177       ierr = MatDestroy(&interpolations[level-1]);CHKERRQ(ierr);
178     } else {
179       P = interpolations[level-1];
180     }
181     ierr = MatCreateVecs(P,&b,&r);CHKERRQ(ierr);
182     ierr = PCMGSetInterpolation(pc,level,P);CHKERRQ(ierr);
183     ierr = PCMGSetRestriction(pc,level,P);CHKERRQ(ierr);
184     ierr = MatDestroy(&P);CHKERRQ(ierr);
185     /* We reuse the matrices when we do not do subspace coarsening */
186     if ((level-1)>=0 && !hmg->subcoarsening) {
187       pmat = operators[level-1];
188       ierr = PCMGSetOperators(pc,level-1,pmat,pmat);CHKERRQ(ierr);
189       ierr = MatDestroy(&pmat);CHKERRQ(ierr);
190     }
191     ierr = PCMGSetRhs(pc,level-1,b);CHKERRQ(ierr);
192 
193     ierr = PCMGSetR(pc,level,r);CHKERRQ(ierr);
194     ierr = VecDestroy(&r);CHKERRQ(ierr);
195 
196     ierr = VecDuplicate(b,&x);CHKERRQ(ierr);
197     ierr = PCMGSetX(pc,level-1,x);CHKERRQ(ierr);
198     ierr = VecDestroy(&x);CHKERRQ(ierr);
199     ierr = VecDestroy(&b);CHKERRQ(ierr);
200   }
201   ierr = PetscFree(interpolations);CHKERRQ(ierr);
202   if (!hmg->subcoarsening) {
203     ierr = PetscFree(operators);CHKERRQ(ierr);
204   }
205   /* Turn Galerkin off when we already have coarse operators */
206   ierr = PCMGSetGalerkin(pc,hmg->subcoarsening ? PC_MG_GALERKIN_PMAT:PC_MG_GALERKIN_NONE);CHKERRQ(ierr);
207   ierr = PCSetDM(pc,NULL);CHKERRQ(ierr);
208   ierr = PCSetUseAmat(pc,PETSC_FALSE);CHKERRQ(ierr);
209   ierr = PetscObjectOptionsBegin((PetscObject)pc);CHKERRQ(ierr);
210   ierr = PCSetFromOptions_MG(PetscOptionsObject,pc);CHKERRQ(ierr); /* should be called in PCSetFromOptions_HMG(), but cannot be called prior to PCMGSetLevels() */
211   ierr = PetscOptionsEnd();CHKERRQ(ierr);
212   ierr = PCSetUp_MG(pc);CHKERRQ(ierr);
213   PetscFunctionReturn(0);
214 }
215 
216 PetscErrorCode PCDestroy_HMG(PC pc)
217 {
218   PetscErrorCode ierr;
219   PC_MG          *mg  = (PC_MG*)pc->data;
220   PC_HMG         *hmg = (PC_HMG*) mg->innerctx;
221 
222   PetscFunctionBegin;
223   ierr = PCDestroy(&hmg->innerpc);CHKERRQ(ierr);
224   ierr = PetscFree(hmg->innerpctype);CHKERRQ(ierr);
225   ierr = PetscFree(hmg);CHKERRQ(ierr);
226   ierr = PCDestroy_MG(pc);CHKERRQ(ierr);
227 
228   ierr = PetscObjectComposeFunction((PetscObject)pc,"PCHMGSetReuseInterpolation_C",NULL);CHKERRQ(ierr);
229   ierr = PetscObjectComposeFunction((PetscObject)pc,"PCHMGSetUseSubspaceCoarsening_C",NULL);CHKERRQ(ierr);
230   ierr = PetscObjectComposeFunction((PetscObject)pc,"PCHMGSetInnerPCType_C",NULL);CHKERRQ(ierr);
231   PetscFunctionReturn(0);
232 }
233 
234 PetscErrorCode PCView_HMG(PC pc,PetscViewer viewer)
235 {
236   PC_MG          *mg = (PC_MG*)pc->data;
237   PC_HMG         *hmg = (PC_HMG*) mg->innerctx;
238   PetscErrorCode ierr;
239   PetscBool      iascii;
240 
241   PetscFunctionBegin;
242   ierr = PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii);CHKERRQ(ierr);
243   if (iascii) {
244     ierr = PetscViewerASCIIPrintf(viewer," Reuse interpolation: %s\n",hmg->reuseinterp? "true":"false");CHKERRQ(ierr);
245     ierr = PetscViewerASCIIPrintf(viewer," Use subspace coarsening: %s\n",hmg->subcoarsening? "true":"false");CHKERRQ(ierr);
246     ierr = PetscViewerASCIIPrintf(viewer," Inner PC type: %s \n",hmg->innerpctype);CHKERRQ(ierr);
247   }
248   ierr = PCView_MG(pc,viewer);CHKERRQ(ierr);
249   PetscFunctionReturn(0);
250 }
251 
252 PetscErrorCode PCSetFromOptions_HMG(PetscOptionItems *PetscOptionsObject,PC pc)
253 {
254   PetscErrorCode ierr;
255   PC_MG          *mg = (PC_MG*)pc->data;
256   PC_HMG         *hmg = (PC_HMG*) mg->innerctx;
257 
258   PetscFunctionBegin;
259   ierr = PetscOptionsHead(PetscOptionsObject,"HMG");CHKERRQ(ierr);
260   ierr = PetscOptionsBool("-pc_hmg_reuse_interpolation","Reuse the interpolation operators when possible (cheaper, weaker when matrix entries change a lot)","None",hmg->reuseinterp,&hmg->reuseinterp,NULL);CHKERRQ(ierr);
261   ierr = PetscOptionsBool("-pc_hmg_use_subspace_coarsening","Use the subspace coarsening to compute the interpolations","None",hmg->subcoarsening,&hmg->subcoarsening,NULL);CHKERRQ(ierr);
262   ierr = PetscOptionsTail();CHKERRQ(ierr);
263   PetscFunctionReturn(0);
264 }
265 
266 static PetscErrorCode PCHMGSetReuseInterpolation_HMG(PC pc, PetscBool reuse)
267 {
268   PC_MG          *mg  = (PC_MG*)pc->data;
269   PC_HMG         *hmg = (PC_HMG*) mg->innerctx;
270 
271   PetscFunctionBegin;
272   hmg->reuseinterp = reuse;
273   PetscFunctionReturn(0);
274 }
275 
276 /*MC
277    PCHMGSetReuseInterpolation - Reuse interpolation matrices in HMG
278 
279    Logically Collective on PC
280 
281    Input Parameters:
282 +  pc - the HMG context
283 -  reuse - True indicates that HMG will reuse the interpolations
284 
285    Options Database Keys:
286 +  -pc_hmg_reuse_interpolation <true | false> - Whether or not to reuse the interpolations. If true, it potentially save the compute time.
287 
288    Level: beginner
289 
290 .keywords: HMG, multigrid, interpolation, reuse, set
291 
292 .seealso: PCHMG
293 M*/
294 PetscErrorCode PCHMGSetReuseInterpolation(PC pc, PetscBool reuse)
295 {
296   PetscErrorCode ierr;
297 
298   PetscFunctionBegin;
299   PetscValidHeaderSpecific(pc,PC_CLASSID,1);
300   ierr = PetscUseMethod(pc,"PCHMGSetReuseInterpolation_C",(PC,PetscBool),(pc,reuse));CHKERRQ(ierr);
301   PetscFunctionReturn(0);
302 }
303 
304 static PetscErrorCode PCHMGSetUseSubspaceCoarsening_HMG(PC pc, PetscBool subspace)
305 {
306   PC_MG          *mg  = (PC_MG*)pc->data;
307   PC_HMG         *hmg = (PC_HMG*) mg->innerctx;
308 
309   PetscFunctionBegin;
310   hmg->subcoarsening = subspace;
311   PetscFunctionReturn(0);
312 }
313 
314 /*MC
315    PCHMGSetUseSubspaceCoarsening - Use subspace coarsening in HMG
316 
317    Logically Collective on PC
318 
319    Input Parameters:
320 +  pc - the HMG context
321 -  reuse - True indicates that HMG will use the subspace coarsening
322 
323    Options Database Keys:
324 +  -pc_hmg_use_subspace_coarsening  <true | false> - Whether or not to use subspace coarsening (that is, coarsen a submatrix).
325 
326    Level: beginner
327 
328 .keywords: HMG, multigrid, interpolation, subspace, coarsening
329 
330 .seealso: PCHMG
331 M*/
332 PetscErrorCode PCHMGSetUseSubspaceCoarsening(PC pc, PetscBool subspace)
333 {
334   PetscErrorCode ierr;
335 
336   PetscFunctionBegin;
337   PetscValidHeaderSpecific(pc,PC_CLASSID,1);
338   ierr = PetscUseMethod(pc,"PCHMGSetUseSubspaceCoarsening_C",(PC,PetscBool),(pc,subspace));CHKERRQ(ierr);
339   PetscFunctionReturn(0);
340 }
341 
342 static PetscErrorCode PCHMGSetInnerPCType_HMG(PC pc, PCType type)
343 {
344   PC_MG           *mg  = (PC_MG*)pc->data;
345   PC_HMG          *hmg = (PC_HMG*) mg->innerctx;
346   PetscErrorCode  ierr;
347 
348   PetscFunctionBegin;
349   ierr = PetscStrallocpy(type,&(hmg->innerpctype));CHKERRQ(ierr);
350   PetscFunctionReturn(0);
351 }
352 
353 /*MC
354    PCHMGSetInnerPCType - Set an inner PC type
355 
356    Logically Collective on PC
357 
358    Input Parameters:
359 +  pc - the HMG context
360 -  type - <hypre, gamg> coarsening algorithm
361 
362    Options Database Keys:
363 +  -hmg_inner_pc_type <hypre, gamg> - What method is used to coarsen matrix
364 
365    Level: beginner
366 
367 .keywords: HMG, multigrid, interpolation, coarsening
368 
369 .seealso: PCHMG, PCType
370 M*/
371 PetscErrorCode PCHMGSetInnerPCType(PC pc, PCType type)
372 {
373   PetscErrorCode  ierr;
374 
375   PetscFunctionBegin;
376   PetscValidHeaderSpecific(pc,PC_CLASSID,1);
377   ierr = PetscUseMethod(pc,"PCHMGSetInnerPCType_C",(PC,PCType),(pc,type));CHKERRQ(ierr);
378   PetscFunctionReturn(0);
379 }
380 
381 /*MC
382    PCHMG - Hybrid of PETSc preconditioners (such as ASM, BJacobi, SOR, etc.) and Hypre BoomerAMG, GAMG or other multilevel methods. BoomerAMG, GAMG
383            or other multilevel methods is used to coarsen matrix and generate a sequence of coarse matrices and interpolations. The matrices and
384            interpolations are employed to construct PCMG, and then any available PETSc preconditioners can be chosen as smoothers and the coarse solver.
385 
386    Options Database Keys:
387 +  -pc_hmg_reuse_interpolation <true | false> - Whether or not to reuse the interpolations. If true, it potentially save the compute time.
388 .  -pc_hmg_use_subspace_coarsening  <true | false> - Whether or not to use subspace coarsening (that is, coarsen a submatrix).
389 .  -hmg_inner_pc_type <hypre, gamg, ...> - What method is used to coarsen matrix
390 
391 
392    Notes:
393     For multicomponent problems, we can just coarsen one submatrix associated with one particular component. In this way, the preconditioner setup
394     time is significantly reduced. One typical use case is neutron transport equations. There are many variables on each mesh vertex due to the
395     of angle and energy. Each variable, in fact, corresponds to the same PDEs but with different material properties.
396 
397    Level: beginner
398 
399    Concepts: Hybrid of ASM and MG, Subspace Coarsening
400 
401     References:
402 +   1. - Fande Kong, Yaqi Wang, Derek R Gaston, Cody J Permann, Andrew E Slaughter, Alexander D Lindsay, Richard C Martineau, A highly parallel multilevel
403     Newton-Krylov-Schwarz method with subspace-based coarsening and partition-based balancing for the multigroup neutron transport equations on
404     3D unstructured meshes, arXiv preprint arXiv:1903.03659, 2019
405 
406 .seealso:  PCCreate(), PCSetType(), PCType, PC, PCMG, PCHYPRE, PCHMG, PCGetCoarseOperators(), PCGetInterpolations(), PCHMGSetReuseInterpolation(), PCHMGSetUseSubspaceCoarsening(),
407            PCHMGSetInnerPCType()
408 
409 M*/
410 PETSC_EXTERN PetscErrorCode PCCreate_HMG(PC pc)
411 {
412   PetscErrorCode ierr;
413   PC_HMG         *hmg;
414   PC_MG          *mg;
415 
416   PetscFunctionBegin;
417   /* if type was previously mg; must manually destroy it because call to PCSetType(pc,PCMG) will not destroy it */
418   if (pc->ops->destroy) {
419     ierr =  (*pc->ops->destroy)(pc);CHKERRQ(ierr);
420     pc->data = 0;
421   }
422   ierr = PetscFree(((PetscObject)pc)->type_name);CHKERRQ(ierr);
423 
424   ierr = PCSetType(pc,PCMG);CHKERRQ(ierr);
425   ierr = PetscNew(&hmg);CHKERRQ(ierr);
426 
427   mg                      = (PC_MG*) pc->data;
428   mg->innerctx            = hmg;
429   hmg->reuseinterp        = PETSC_FALSE;
430   hmg->subcoarsening      = PETSC_FALSE;
431   hmg->innerpc            = NULL;
432 
433   pc->ops->setfromoptions = PCSetFromOptions_HMG;
434   pc->ops->view           = PCView_HMG;
435   pc->ops->destroy        = PCDestroy_HMG;
436   pc->ops->setup          = PCSetUp_HMG;
437 
438   ierr = PetscObjectComposeFunction((PetscObject)pc,"PCHMGSetReuseInterpolation_C",PCHMGSetReuseInterpolation_HMG);CHKERRQ(ierr);
439   ierr = PetscObjectComposeFunction((PetscObject)pc,"PCHMGSetUseSubspaceCoarsening_C",PCHMGSetUseSubspaceCoarsening_HMG);CHKERRQ(ierr);
440   ierr = PetscObjectComposeFunction((PetscObject)pc,"PCHMGSetInnerPCType_C",PCHMGSetInnerPCType_HMG);CHKERRQ(ierr);
441   PetscFunctionReturn(0);
442 }
443