So, I want to solve a system Ax = b multiple times, reuse A with different bs.
It is natural to set differents initial guesses each time for iterative
solvers.
And
KSPSetComputeInitialGuess(ksp,ComputeGuess,&user);
is what PETSc provide for setting the Guess.
I am unsure if I am making something wrong, but it seems to me that this
function is called only once the matrix is first setted up. I want to be
called each time.
See my modified ex32.c for comparison.
./ex32 -pc_type mg -pc_mg_type full -ksp_type fgmres -ksp_monitor_short -
pc_mg_levels 3 -mg_coarse_pc_factor_shift_type nonzero -
ksp_initial_guess_nonzero
Thanks.
Best,
Filippo
/*T
* Concepts: KSP^solving a system of linear equations
* Concepts: KSP^Laplacian, 2d
* Processors: n
T **/
/*
L *aplacian in 2D. Modeled by the partial differential equation
div grad u = f, 0 < x,y < 1,
with forcing function
f = e^{-(1 - x)^2/\nu} e^{-(1 - y)^2/\nu}
with pure Neumann boundary conditions
The functions are cell-centered
This uses multigrid to solve the linear system
Contributed by Andrei Draganescu <[email protected]>
Note the nice multigrid convergence despite the fact it is only using
peicewise constant interpolation/restriction. This is because cell-centered multigrid
does not need the same rule:
polynomial degree(interpolation) + polynomial degree(restriction) + 2 > degree of PDE
that vertex based multigrid needs.
*/
static char help[] = "Solves 2D inhomogeneous Laplacian using multigrid.\n\n";
#include <petscdm.h>
#include <petscdmda.h>
#include <petscksp.h>
extern PetscErrorCode ComputeMatrix(KSP,Mat,Mat,void*);
extern PetscErrorCode ComputeRHS(KSP,Vec,void*);
extern PetscErrorCode ComputeGuess(KSP,Vec,void*);
typedef enum {DIRICHLET, NEUMANN} BCType;
typedef struct {
PetscScalar nu;
BCType bcType;
} UserContext;
#undef __FUNCT__
#define __FUNCT__ "main"
int main(int argc,char **argv)
{
KSP ksp;
DM da;
UserContext user;
const char *bcTypes[2] = {"dirichlet","neumann"};
PetscErrorCode ierr;
PetscInt bc;
PetscInitialize(&argc,&argv,(char*)0,help);
ierr = KSPCreate(PETSC_COMM_WORLD,&ksp);CHKERRQ(ierr);
ierr = DMDACreate2d(PETSC_COMM_WORLD, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE,DMDA_STENCIL_STAR,12,12,PETSC_DECIDE,PETSC_DECIDE,1,1,0,0,&da);CHKERRQ(ierr);
ierr = DMDASetInterpolationType(da, DMDA_Q0);CHKERRQ(ierr);
ierr = KSPSetDM(ksp,da);CHKERRQ(ierr);
ierr = PetscOptionsBegin(PETSC_COMM_WORLD, "", "Options for the inhomogeneous Poisson equation", "DM");
user.nu = 0.1;
ierr = PetscOptionsScalar("-nu", "The width of the Gaussian source", "ex29.c", 0.1, &user.nu, NULL);CHKERRQ(ierr);
bc = (PetscInt)NEUMANN;
ierr = PetscOptionsEList("-bc_type","Type of boundary condition","ex29.c",bcTypes,2,bcTypes[0],&bc,NULL);CHKERRQ(ierr);
user.bcType = (BCType)bc;
ierr = PetscOptionsEnd();
ierr = KSPSetComputeRHS(ksp,ComputeRHS,&user);CHKERRQ(ierr);
ierr = KSPSetComputeInitialGuess(ksp,ComputeGuess,&user);CHKERRQ(ierr);
ierr = KSPSetComputeOperators(ksp,ComputeMatrix,&user);CHKERRQ(ierr);
ierr = KSPSetFromOptions(ksp);CHKERRQ(ierr);
for(int i = 0; i < 9; ++i) {
printf("KSPSolve\n");
// KSPSetUp(ksp);
// ierr = KSPSetComputeInitialGuess(ksp,ComputeGuess,&user);CHKERRQ(ierr);
ierr = KSPSolve(ksp,NULL,NULL);CHKERRQ(ierr);
}
ierr = KSPDestroy(&ksp);CHKERRQ(ierr);
ierr = DMDestroy(&da);CHKERRQ(ierr);
ierr = PetscFinalize();
return 0;
}
#undef __FUNCT__
#define __FUNCT__ "ComputeGuess"
PetscErrorCode ComputeGuess(KSP ksp,Vec b,void *ctx)
{
printf("ComputeGuess\n");
VecSet(b, 1);
PetscFunctionReturn(0);
}
#undef __FUNCT__
#define __FUNCT__ "ComputeRHS"
PetscErrorCode ComputeRHS(KSP ksp,Vec b,void *ctx)
{
printf("ComputeRHS\n");
UserContext *user = (UserContext*)ctx;
PetscErrorCode ierr;
PetscInt i,j,mx,my,xm,ym,xs,ys;
PetscScalar Hx,Hy;
PetscScalar **array;
DM da;
PetscFunctionBeginUser;
ierr = KSPGetDM(ksp,&da);CHKERRQ(ierr);
ierr = DMDAGetInfo(da, 0, &mx, &my, 0,0,0,0,0,0,0,0,0,0);CHKERRQ(ierr);
Hx = 1.0 / (PetscReal)(mx);
Hy = 1.0 / (PetscReal)(my);
ierr = DMDAGetCorners(da,&xs,&ys,0,&xm,&ym,0);CHKERRQ(ierr);
ierr = DMDAVecGetArray(da, b, &array);CHKERRQ(ierr);
for (j=ys; j<ys+ym; j++) {
for (i=xs; i<xs+xm; i++) {
array[j][i] = PetscExpScalar(-(((PetscReal)i+0.5)*Hx)*(((PetscReal)i+0.5)*Hx)/user->nu)*PetscExpScalar(-(((PetscReal)j+0.5)*Hy)*(((PetscReal)j+0.5)*Hy)/user->nu)*Hx*Hy;
}
}
ierr = DMDAVecRestoreArray(da, b, &array);CHKERRQ(ierr);
ierr = VecAssemblyBegin(b);CHKERRQ(ierr);
ierr = VecAssemblyEnd(b);CHKERRQ(ierr);
/* force right hand side to be consistent for singular matrix */
/* note this is really a hack, normally the model would provide you with a consistent right handside */
if (user->bcType == NEUMANN) {
MatNullSpace nullspace;
ierr = MatNullSpaceCreate(PETSC_COMM_WORLD,PETSC_TRUE,0,0,&nullspace);CHKERRQ(ierr);
ierr = MatNullSpaceRemove(nullspace,b);CHKERRQ(ierr);
ierr = MatNullSpaceDestroy(&nullspace);CHKERRQ(ierr);
}
PetscFunctionReturn(0);
}
#undef __FUNCT__
#define __FUNCT__ "ComputeMatrix"
PetscErrorCode ComputeMatrix(KSP ksp, Mat J,Mat jac, void *ctx)
{
printf("ComputeMatrix\n");
UserContext *user = (UserContext*)ctx;
PetscErrorCode ierr;
PetscInt i,j,mx,my,xm,ym,xs,ys,num, numi, numj;
PetscScalar v[5],Hx,Hy,HydHx,HxdHy;
MatStencil row, col[5];
DM da;
PetscFunctionBeginUser;
ierr = KSPGetDM(ksp,&da);CHKERRQ(ierr);
ierr = DMDAGetInfo(da,0,&mx,&my,0,0,0,0,0,0,0,0,0,0);CHKERRQ(ierr);
Hx = 1.0 / (PetscReal)(mx);
Hy = 1.0 / (PetscReal)(my);
HxdHy = Hx/Hy;
HydHx = Hy/Hx;
ierr = DMDAGetCorners(da,&xs,&ys,0,&xm,&ym,0);CHKERRQ(ierr);
for (j=ys; j<ys+ym; j++) {
for (i=xs; i<xs+xm; i++) {
row.i = i; row.j = j;
if (i==0 || j==0 || i==mx-1 || j==my-1) {
if (user->bcType == DIRICHLET) {
v[0] = 2.0*(HxdHy + HydHx);
ierr = MatSetValuesStencil(jac,1,&row,1,&row,v,INSERT_VALUES);CHKERRQ(ierr);
SETERRQ(PETSC_COMM_WORLD,PETSC_ERR_SUP,"Dirichlet boundary conditions not supported !\n");
} else if (user->bcType == NEUMANN) {
num = 0; numi=0; numj=0;
if (j!=0) {
v[num] = -HxdHy;
col[num].i = i;
col[num].j = j-1;
num++; numj++;
}
if (i!=0) {
v[num] = -HydHx;
col[num].i = i-1;
col[num].j = j;
num++; numi++;
}
if (i!=mx-1) {
v[num] = -HydHx;
col[num].i = i+1;
col[num].j = j;
num++; numi++;
}
if (j!=my-1) {
v[num] = -HxdHy;
col[num].i = i;
col[num].j = j+1;
num++; numj++;
}
v[num] = (PetscReal)(numj)*HxdHy + (PetscReal)(numi)*HydHx; col[num].i = i; col[num].j = j;
num++;
ierr = MatSetValuesStencil(jac,1,&row,num,col,v,INSERT_VALUES);CHKERRQ(ierr);
}
} else {
v[0] = -HxdHy; col[0].i = i; col[0].j = j-1;
v[1] = -HydHx; col[1].i = i-1; col[1].j = j;
v[2] = 2.0*(HxdHy + HydHx); col[2].i = i; col[2].j = j;
v[3] = -HydHx; col[3].i = i+1; col[3].j = j;
v[4] = -HxdHy; col[4].i = i; col[4].j = j+1;
ierr = MatSetValuesStencil(jac,1,&row,5,col,v,INSERT_VALUES);CHKERRQ(ierr);
}
}
}
ierr = MatAssemblyBegin(jac,MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
ierr = MatAssemblyEnd(jac,MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
if (user->bcType == NEUMANN) {
MatNullSpace nullspace;
ierr = MatNullSpaceCreate(PETSC_COMM_WORLD,PETSC_TRUE,0,0,&nullspace);CHKERRQ(ierr);
ierr = MatSetNullSpace(jac,nullspace);CHKERRQ(ierr);
ierr = MatNullSpaceDestroy(&nullspace);CHKERRQ(ierr);
}
PetscFunctionReturn(0);
}