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