#include /*I "petscksp.h" I*/ /* ---------------------------------------------------------------------------*/ #undef __FUNCT__ #define __FUNCT__ "PCMGResidualDefault" /*@C PCMGResidualDefault - Default routine to calculate the residual. Collective on Mat and Vec Input Parameters: + mat - the matrix . b - the right-hand-side - x - the approximate solution Output Parameter: . r - location to store the residual Level: developer .keywords: MG, default, multigrid, residual .seealso: PCMGSetResidual() @*/ PetscErrorCode PCMGResidualDefault(Mat mat,Vec b,Vec x,Vec r) { PetscErrorCode ierr; PetscFunctionBegin; ierr = MatResidual(mat,b,x,r);CHKERRQ(ierr); PetscFunctionReturn(0); } #undef __FUNCT__ #define __FUNCT__ "PCMGGetCoarseSolve" /*@ PCMGGetCoarseSolve - Gets the solver context to be used on the coarse grid. Not Collective Input Parameter: . pc - the multigrid context Output Parameter: . ksp - the coarse grid solver context Level: advanced .keywords: MG, multigrid, get, coarse grid @*/ PetscErrorCode PCMGGetCoarseSolve(PC pc,KSP *ksp) { PC_MG *mg = (PC_MG*)pc->data; PC_MG_Levels **mglevels = mg->levels; PetscFunctionBegin; PetscValidHeaderSpecific(pc,PC_CLASSID,1); *ksp = mglevels[0]->smoothd; PetscFunctionReturn(0); } #undef __FUNCT__ #define __FUNCT__ "PCMGSetResidual" /*@C PCMGSetResidual - Sets the function to be used to calculate the residual on the lth level. Logically Collective on PC and Mat Input Parameters: + pc - the multigrid context . l - the level (0 is coarsest) to supply . residual - function used to form residual, if none is provided the previously provide one is used, if no previous one were provided then a default is used - mat - matrix associated with residual Level: advanced .keywords: MG, set, multigrid, residual, level .seealso: PCMGResidualDefault() @*/ PetscErrorCode PCMGSetResidual(PC pc,PetscInt l,PetscErrorCode (*residual)(Mat,Vec,Vec,Vec),Mat mat) { PC_MG *mg = (PC_MG*)pc->data; PC_MG_Levels **mglevels = mg->levels; PetscErrorCode ierr; PetscFunctionBegin; PetscValidHeaderSpecific(pc,PC_CLASSID,1); if (!mglevels) SETERRQ(PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_WRONGSTATE,"Must set MG levels before calling"); if (residual) mglevels[l]->residual = residual; if (!mglevels[l]->residual) mglevels[l]->residual = PCMGResidualDefault; if (mat) {ierr = PetscObjectReference((PetscObject)mat);CHKERRQ(ierr);} ierr = MatDestroy(&mglevels[l]->A);CHKERRQ(ierr); mglevels[l]->A = mat; PetscFunctionReturn(0); } #undef __FUNCT__ #define __FUNCT__ "PCMGSetInterpolation" /*@ PCMGSetInterpolation - Sets the function to be used to calculate the interpolation from l-1 to the lth level Logically Collective on PC and Mat Input Parameters: + pc - the multigrid context . mat - the interpolation operator - l - the level (0 is coarsest) to supply [do not supply 0] Level: advanced Notes: Usually this is the same matrix used also to set the restriction for the same level. One can pass in the interpolation matrix or its transpose; PETSc figures out from the matrix size which one it is. .keywords: multigrid, set, interpolate, level .seealso: PCMGSetRestriction() @*/ PetscErrorCode PCMGSetInterpolation(PC pc,PetscInt l,Mat mat) { PC_MG *mg = (PC_MG*)pc->data; PC_MG_Levels **mglevels = mg->levels; PetscErrorCode ierr; PetscFunctionBegin; PetscValidHeaderSpecific(pc,PC_CLASSID,1); if (!mglevels) SETERRQ(PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_WRONGSTATE,"Must set MG levels before calling"); if (!l) SETERRQ(PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_OUTOFRANGE,"Do not set interpolation routine for coarsest level"); ierr = PetscObjectReference((PetscObject)mat);CHKERRQ(ierr); ierr = MatDestroy(&mglevels[l]->interpolate);CHKERRQ(ierr); mglevels[l]->interpolate = mat; PetscFunctionReturn(0); } #undef __FUNCT__ #define __FUNCT__ "PCMGGetInterpolation" /*@ PCMGGetInterpolation - Gets the function to be used to calculate the interpolation from l-1 to the lth level Logically Collective on PC Input Parameters: + pc - the multigrid context - l - the level (0 is coarsest) to supply [Do not supply 0] Output Parameter: . mat - the interpolation matrix Level: advanced .keywords: MG, get, multigrid, interpolation, level .seealso: PCMGGetRestriction(), PCMGSetInterpolation(), PCMGGetRScale() @*/ PetscErrorCode PCMGGetInterpolation(PC pc,PetscInt l,Mat *mat) { PC_MG *mg = (PC_MG*)pc->data; PC_MG_Levels **mglevels = mg->levels; PetscErrorCode ierr; PetscFunctionBegin; PetscValidHeaderSpecific(pc,PC_CLASSID,1); PetscValidPointer(mat,3); if (!mglevels) SETERRQ(PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_WRONGSTATE,"Must set MG levels before calling"); if (l <= 0 || mg->nlevels <= l) SETERRQ2(PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_OUTOFRANGE,"Level %D must be in range {1,...,%D}",l,mg->nlevels-1); if (!mglevels[l]->interpolate) { if (!mglevels[l]->restrct) SETERRQ(PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_WRONGSTATE,"Must call PCMGSetInterpolation() or PCMGSetInterpolation()"); ierr = PCMGSetInterpolation(pc,l,mglevels[l]->restrct);CHKERRQ(ierr); } *mat = mglevels[l]->interpolate; PetscFunctionReturn(0); } #undef __FUNCT__ #define __FUNCT__ "PCMGSetRestriction" /*@ PCMGSetRestriction - Sets the function to be used to restrict vector from level l to l-1. Logically Collective on PC and Mat Input Parameters: + pc - the multigrid context . l - the level (0 is coarsest) to supply [Do not supply 0] - mat - the restriction matrix Level: advanced Notes: Usually this is the same matrix used also to set the interpolation for the same level. One can pass in the interpolation matrix or its transpose; PETSc figures out from the matrix size which one it is. If you do not set this, the transpose of the Mat set with PCMGSetInterpolation() is used. .keywords: MG, set, multigrid, restriction, level .seealso: PCMGSetInterpolation() @*/ PetscErrorCode PCMGSetRestriction(PC pc,PetscInt l,Mat mat) { PetscErrorCode ierr; PC_MG *mg = (PC_MG*)pc->data; PC_MG_Levels **mglevels = mg->levels; PetscFunctionBegin; PetscValidHeaderSpecific(pc,PC_CLASSID,1); PetscValidHeaderSpecific(mat,MAT_CLASSID,3); if (!mglevels) SETERRQ(PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_WRONGSTATE,"Must set MG levels before calling"); if (!l) SETERRQ(PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_OUTOFRANGE,"Do not set restriction routine for coarsest level"); ierr = PetscObjectReference((PetscObject)mat);CHKERRQ(ierr); ierr = MatDestroy(&mglevels[l]->restrct);CHKERRQ(ierr); mglevels[l]->restrct = mat; PetscFunctionReturn(0); } #undef __FUNCT__ #define __FUNCT__ "PCMGGetRestriction" /*@ PCMGGetRestriction - Gets the function to be used to restrict vector from level l to l-1. Logically Collective on PC and Mat Input Parameters: + pc - the multigrid context - l - the level (0 is coarsest) to supply [Do not supply 0] Output Parameter: . mat - the restriction matrix Level: advanced .keywords: MG, get, multigrid, restriction, level .seealso: PCMGGetInterpolation(), PCMGSetRestriction(), PCMGGetRScale() @*/ PetscErrorCode PCMGGetRestriction(PC pc,PetscInt l,Mat *mat) { PC_MG *mg = (PC_MG*)pc->data; PC_MG_Levels **mglevels = mg->levels; PetscErrorCode ierr; PetscFunctionBegin; PetscValidHeaderSpecific(pc,PC_CLASSID,1); PetscValidPointer(mat,3); if (!mglevels) SETERRQ(PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_WRONGSTATE,"Must set MG levels before calling"); if (l <= 0 || mg->nlevels <= l) SETERRQ2(PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_OUTOFRANGE,"Level %D must be in range {1,...,%D}",l,mg->nlevels-1); if (!mglevels[l]->restrct) { if (!mglevels[l]->interpolate) SETERRQ(PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_WRONGSTATE,"Must call PCMGSetRestriction() or PCMGSetInterpolation()"); ierr = PCMGSetRestriction(pc,l,mglevels[l]->interpolate);CHKERRQ(ierr); } *mat = mglevels[l]->restrct; PetscFunctionReturn(0); } #undef __FUNCT__ #define __FUNCT__ "PCMGSetRScale" /*@ PCMGSetRScale - Sets the pointwise scaling for the restriction operator from level l to l-1. Logically Collective on PC and Vec Input Parameters: + pc - the multigrid context - l - the level (0 is coarsest) to supply [Do not supply 0] . rscale - the scaling Level: advanced Notes: When evaluating a function on a coarse level one does not want to do F(R * x) one does F(rscale * R * x) where rscale is 1 over the row sums of R. .keywords: MG, set, multigrid, restriction, level .seealso: PCMGSetInterpolation(), PCMGSetRestriction(), PCMGGetRScale() @*/ PetscErrorCode PCMGSetRScale(PC pc,PetscInt l,Vec rscale) { PetscErrorCode ierr; PC_MG *mg = (PC_MG*)pc->data; PC_MG_Levels **mglevels = mg->levels; PetscFunctionBegin; PetscValidHeaderSpecific(pc,PC_CLASSID,1); if (!mglevels) SETERRQ(PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_WRONGSTATE,"Must set MG levels before calling"); if (l <= 0 || mg->nlevels <= l) SETERRQ2(PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_OUTOFRANGE,"Level %D must be in range {1,...,%D}",l,mg->nlevels-1); ierr = PetscObjectReference((PetscObject)rscale);CHKERRQ(ierr); ierr = VecDestroy(&mglevels[l]->rscale);CHKERRQ(ierr); mglevels[l]->rscale = rscale; PetscFunctionReturn(0); } #undef __FUNCT__ #define __FUNCT__ "PCMGGetRScale" /*@ PCMGGetRScale - Gets the pointwise scaling for the restriction operator from level l to l-1. Collective on PC Input Parameters: + pc - the multigrid context . rscale - the scaling - l - the level (0 is coarsest) to supply [Do not supply 0] Level: advanced Notes: When evaluating a function on a coarse level one does not want to do F(R * x) one does F(rscale * R * x) where rscale is 1 over the row sums of R. .keywords: MG, set, multigrid, restriction, level .seealso: PCMGSetInterpolation(), PCMGGetRestriction() @*/ PetscErrorCode PCMGGetRScale(PC pc,PetscInt l,Vec *rscale) { PetscErrorCode ierr; PC_MG *mg = (PC_MG*)pc->data; PC_MG_Levels **mglevels = mg->levels; PetscFunctionBegin; PetscValidHeaderSpecific(pc,PC_CLASSID,1); if (!mglevels) SETERRQ(PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_WRONGSTATE,"Must set MG levels before calling"); if (l <= 0 || mg->nlevels <= l) SETERRQ2(PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_OUTOFRANGE,"Level %D must be in range {1,...,%D}",l,mg->nlevels-1); if (!mglevels[l]->rscale) { Mat R; Vec X,Y,coarse,fine; PetscInt M,N; ierr = PCMGGetRestriction(pc,l,&R);CHKERRQ(ierr); ierr = MatCreateVecs(R,&X,&Y);CHKERRQ(ierr); ierr = MatGetSize(R,&M,&N);CHKERRQ(ierr); if (M < N) { fine = X; coarse = Y; } else if (N < M) { fine = Y; coarse = X; } else SETERRQ(PetscObjectComm((PetscObject)R),PETSC_ERR_SUP,"Restriction matrix is square, cannot determine which Vec is coarser"); ierr = VecSet(fine,1.);CHKERRQ(ierr); ierr = MatRestrict(R,fine,coarse);CHKERRQ(ierr); ierr = VecDestroy(&fine);CHKERRQ(ierr); ierr = VecReciprocal(coarse);CHKERRQ(ierr); mglevels[l]->rscale = coarse; } *rscale = mglevels[l]->rscale; PetscFunctionReturn(0); } #undef __FUNCT__ #define __FUNCT__ "PCMGGetSmoother" /*@ PCMGGetSmoother - Gets the KSP context to be used as smoother for both pre- and post-smoothing. Call both PCMGGetSmootherUp() and PCMGGetSmootherDown() to use different functions for pre- and post-smoothing. Not Collective, KSP returned is parallel if PC is Input Parameters: + pc - the multigrid context - l - the level (0 is coarsest) to supply Ouput Parameters: . ksp - the smoother Notes: Once you have called this routine, you can call KSPSetOperators(ksp,...) on the resulting ksp to provide the operators for the smoother for this level. You can also modify smoother options by calling the various KSPSetXXX() options on this ksp. In addition you can call KSPGetPC(ksp,&pc) and modify PC options for the smoother; for example PCSetType(pc,PCSOR); to use SOR smoothing. Level: advanced .keywords: MG, get, multigrid, level, smoother, pre-smoother, post-smoother .seealso: PCMGGetSmootherUp(), PCMGGetSmootherDown() @*/ PetscErrorCode PCMGGetSmoother(PC pc,PetscInt l,KSP *ksp) { PC_MG *mg = (PC_MG*)pc->data; PC_MG_Levels **mglevels = mg->levels; PetscFunctionBegin; PetscValidHeaderSpecific(pc,PC_CLASSID,1); *ksp = mglevels[l]->smoothd; PetscFunctionReturn(0); } #undef __FUNCT__ #define __FUNCT__ "PCMGGetSmootherUp" /*@ PCMGGetSmootherUp - Gets the KSP context to be used as smoother after coarse grid correction (post-smoother). Not Collective, KSP returned is parallel if PC is Input Parameters: + pc - the multigrid context - l - the level (0 is coarsest) to supply Ouput Parameters: . ksp - the smoother Level: advanced Notes: calling this will result in a different pre and post smoother so you may need to set options on the pre smoother also .keywords: MG, multigrid, get, smoother, up, post-smoother, level .seealso: PCMGGetSmootherUp(), PCMGGetSmootherDown() @*/ PetscErrorCode PCMGGetSmootherUp(PC pc,PetscInt l,KSP *ksp) { PC_MG *mg = (PC_MG*)pc->data; PC_MG_Levels **mglevels = mg->levels; PetscErrorCode ierr; const char *prefix; MPI_Comm comm; PetscFunctionBegin; PetscValidHeaderSpecific(pc,PC_CLASSID,1); /* This is called only if user wants a different pre-smoother from post. Thus we check if a different one has already been allocated, if not we allocate it. */ if (!l) SETERRQ(PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_OUTOFRANGE,"There is no such thing as a up smoother on the coarse grid"); if (mglevels[l]->smoothu == mglevels[l]->smoothd) { KSPType ksptype; PCType pctype; PC ipc; PetscReal rtol,abstol,dtol; PetscInt maxits; KSPNormType normtype; ierr = PetscObjectGetComm((PetscObject)mglevels[l]->smoothd,&comm);CHKERRQ(ierr); ierr = KSPGetOptionsPrefix(mglevels[l]->smoothd,&prefix);CHKERRQ(ierr); ierr = KSPGetTolerances(mglevels[l]->smoothd,&rtol,&abstol,&dtol,&maxits);CHKERRQ(ierr); ierr = KSPGetType(mglevels[l]->smoothd,&ksptype);CHKERRQ(ierr); ierr = KSPGetNormType(mglevels[l]->smoothd,&normtype);CHKERRQ(ierr); ierr = KSPGetPC(mglevels[l]->smoothd,&ipc);CHKERRQ(ierr); ierr = PCGetType(ipc,&pctype);CHKERRQ(ierr); ierr = KSPCreate(comm,&mglevels[l]->smoothu);CHKERRQ(ierr); ierr = KSPSetErrorIfNotConverged(mglevels[l]->smoothu,pc->erroriffailure);CHKERRQ(ierr); ierr = PetscObjectIncrementTabLevel((PetscObject)mglevels[l]->smoothu,(PetscObject)pc,mglevels[0]->levels-l);CHKERRQ(ierr); ierr = KSPSetOptionsPrefix(mglevels[l]->smoothu,prefix);CHKERRQ(ierr); ierr = KSPSetTolerances(mglevels[l]->smoothu,rtol,abstol,dtol,maxits);CHKERRQ(ierr); ierr = KSPSetType(mglevels[l]->smoothu,ksptype);CHKERRQ(ierr); ierr = KSPSetNormType(mglevels[l]->smoothu,normtype);CHKERRQ(ierr); ierr = KSPSetConvergenceTest(mglevels[l]->smoothu,KSPConvergedSkip,NULL,NULL);CHKERRQ(ierr); ierr = KSPGetPC(mglevels[l]->smoothu,&ipc);CHKERRQ(ierr); ierr = PCSetType(ipc,pctype);CHKERRQ(ierr); ierr = PetscLogObjectParent((PetscObject)pc,(PetscObject)mglevels[l]->smoothu);CHKERRQ(ierr); } if (ksp) *ksp = mglevels[l]->smoothu; PetscFunctionReturn(0); } #undef __FUNCT__ #define __FUNCT__ "PCMGGetSmootherDown" /*@ PCMGGetSmootherDown - Gets the KSP context to be used as smoother before coarse grid correction (pre-smoother). Not Collective, KSP returned is parallel if PC is Input Parameters: + pc - the multigrid context - l - the level (0 is coarsest) to supply Ouput Parameters: . ksp - the smoother Level: advanced Notes: calling this will result in a different pre and post smoother so you may need to set options on the post smoother also .keywords: MG, multigrid, get, smoother, down, pre-smoother, level .seealso: PCMGGetSmootherUp(), PCMGGetSmoother() @*/ PetscErrorCode PCMGGetSmootherDown(PC pc,PetscInt l,KSP *ksp) { PetscErrorCode ierr; PC_MG *mg = (PC_MG*)pc->data; PC_MG_Levels **mglevels = mg->levels; PetscFunctionBegin; PetscValidHeaderSpecific(pc,PC_CLASSID,1); /* make sure smoother up and down are different */ if (l) { ierr = PCMGGetSmootherUp(pc,l,NULL);CHKERRQ(ierr); } *ksp = mglevels[l]->smoothd; PetscFunctionReturn(0); } #undef __FUNCT__ #define __FUNCT__ "PCMGSetCyclesOnLevel" /*@ PCMGSetCyclesOnLevel - Sets the number of cycles to run on this level. Logically Collective on PC Input Parameters: + pc - the multigrid context . l - the level (0 is coarsest) this is to be used for - n - the number of cycles Level: advanced .keywords: MG, multigrid, set, cycles, V-cycle, W-cycle, level .seealso: PCMGSetCycles() @*/ PetscErrorCode PCMGSetCyclesOnLevel(PC pc,PetscInt l,PetscInt c) { PC_MG *mg = (PC_MG*)pc->data; PC_MG_Levels **mglevels = mg->levels; PetscFunctionBegin; PetscValidHeaderSpecific(pc,PC_CLASSID,1); if (!mglevels) SETERRQ(PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_WRONGSTATE,"Must set MG levels before calling"); PetscValidLogicalCollectiveInt(pc,l,2); PetscValidLogicalCollectiveInt(pc,c,3); mglevels[l]->cycles = c; PetscFunctionReturn(0); } #undef __FUNCT__ #define __FUNCT__ "PCMGSetRhs" /*@ PCMGSetRhs - Sets the vector space to be used to store the right-hand side on a particular level. Logically Collective on PC and Vec Input Parameters: + pc - the multigrid context . l - the level (0 is coarsest) this is to be used for - c - the space Level: advanced Notes: If this is not provided PETSc will automatically generate one. You do not need to keep a reference to this vector if you do not need it PCDestroy() will properly free it. .keywords: MG, multigrid, set, right-hand-side, rhs, level .seealso: PCMGSetX(), PCMGSetR() @*/ PetscErrorCode PCMGSetRhs(PC pc,PetscInt l,Vec c) { PetscErrorCode ierr; PC_MG *mg = (PC_MG*)pc->data; PC_MG_Levels **mglevels = mg->levels; PetscFunctionBegin; PetscValidHeaderSpecific(pc,PC_CLASSID,1); if (!mglevels) SETERRQ(PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_WRONGSTATE,"Must set MG levels before calling"); if (l == mglevels[0]->levels-1) SETERRQ(PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_INCOMP,"Do not set rhs for finest level"); ierr = PetscObjectReference((PetscObject)c);CHKERRQ(ierr); ierr = VecDestroy(&mglevels[l]->b);CHKERRQ(ierr); mglevels[l]->b = c; PetscFunctionReturn(0); } #undef __FUNCT__ #define __FUNCT__ "PCMGSetX" /*@ PCMGSetX - Sets the vector space to be used to store the solution on a particular level. Logically Collective on PC and Vec Input Parameters: + pc - the multigrid context . l - the level (0 is coarsest) this is to be used for (do not supply the finest level) - c - the space Level: advanced Notes: If this is not provided PETSc will automatically generate one. You do not need to keep a reference to this vector if you do not need it PCDestroy() will properly free it. .keywords: MG, multigrid, set, solution, level .seealso: PCMGSetRhs(), PCMGSetR() @*/ PetscErrorCode PCMGSetX(PC pc,PetscInt l,Vec c) { PetscErrorCode ierr; PC_MG *mg = (PC_MG*)pc->data; PC_MG_Levels **mglevels = mg->levels; PetscFunctionBegin; PetscValidHeaderSpecific(pc,PC_CLASSID,1); if (!mglevels) SETERRQ(PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_WRONGSTATE,"Must set MG levels before calling"); if (l == mglevels[0]->levels-1) SETERRQ(PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_INCOMP,"Do not set x for finest level"); ierr = PetscObjectReference((PetscObject)c);CHKERRQ(ierr); ierr = VecDestroy(&mglevels[l]->x);CHKERRQ(ierr); mglevels[l]->x = c; PetscFunctionReturn(0); } #undef __FUNCT__ #define __FUNCT__ "PCMGSetR" /*@ PCMGSetR - Sets the vector space to be used to store the residual on a particular level. Logically Collective on PC and Vec Input Parameters: + pc - the multigrid context . l - the level (0 is coarsest) this is to be used for - c - the space Level: advanced Notes: If this is not provided PETSc will automatically generate one. You do not need to keep a reference to this vector if you do not need it PCDestroy() will properly free it. .keywords: MG, multigrid, set, residual, level @*/ PetscErrorCode PCMGSetR(PC pc,PetscInt l,Vec c) { PetscErrorCode ierr; PC_MG *mg = (PC_MG*)pc->data; PC_MG_Levels **mglevels = mg->levels; PetscFunctionBegin; PetscValidHeaderSpecific(pc,PC_CLASSID,1); if (!mglevels) SETERRQ(PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_WRONGSTATE,"Must set MG levels before calling"); if (!l) SETERRQ(PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_OUTOFRANGE,"Need not set residual vector for coarse grid"); ierr = PetscObjectReference((PetscObject)c);CHKERRQ(ierr); ierr = VecDestroy(&mglevels[l]->r);CHKERRQ(ierr); mglevels[l]->r = c; PetscFunctionReturn(0); }