/*@ MatCreateSchurComplementPmat - create a preconditioning matrix for the Schur complement by assembling Sp = A11 - A10 inv(diag(A00)) A01 Collective on Mat Input Parameters: + A00,A01,A10,A11 - the four parts of the original matrix A = [A00 A01; A10 A11] (A01,A10, and A11 are optional, implying zero matrices) . ainvtype - type of approximation for inv(A00) used when forming Sp = A11 - A10 inv(A00) A01 - preuse - MAT_INITIAL_MATRIX for a new Sp, or MAT_REUSE_MATRIX to reuse an existing Sp, or MAT_IGNORE_MATRIX to put nothing in Sp Output Parameter: - Spmat - approximate Schur complement suitable for preconditioning S = A11 - A10 inv(diag(A00)) A01 Note: Since the real Schur complement is usually dense, providing a good approximation to newpmat usually requires application-specific information. The default for assembled matrices is to use the inverse of the diagonal of the (0,0) block A00 in place of A00^{-1}. This rarely produce a scalable algorithm. Optionally, A00 can be lumped before forming inv(diag(A00)). Level: advanced Concepts: matrices^submatrices .seealso: MatCreateSchurComplement(), MatGetSchurComplement(), MatSchurComplementGetPmat(), MatSchurComplementAinvType @*/ PetscErrorCode MatCreateSchurComplementPmat(Mat A00,Mat A01,Mat A10,Mat A11,MatSchurComplementAinvType ainvtype,MatReuse preuse,Mat *Spmat) { PetscErrorCode ierr; PetscInt N00; PetscFunctionBegin; /* Use an appropriate approximate inverse of A00 to form A11 - A10 inv(diag(A00)) A01; a NULL A01, A10 or A11 indicates a zero matrix. */ /* TODO: Perhaps should create an appropriately-sized zero matrix of the same type as A00? */ if ((!A01 || !A10) & !A11) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONGSTATE,"Cannot assemble Spmat: A01, A10 and A11 are all NULL."); if (preuse == MAT_IGNORE_MATRIX) PetscFunctionReturn(0); /* A zero size A00 or empty A01 or A10 imply S = A11. */ ierr = MatGetSize(A00,&N00,NULL);CHKERRQ(ierr); if (!A01 || !A10 || !N00) { if (preuse == MAT_INITIAL_MATRIX) { ierr = MatDuplicate(A11,MAT_COPY_VALUES,Spmat);CHKERRQ(ierr); } else { /* MAT_REUSE_MATRIX */ /* TODO: when can we pass SAME_NONZERO_PATTERN? */ ierr = MatCopy(A11,*Spmat,DIFFERENT_NONZERO_PATTERN);CHKERRQ(ierr); } } else { Mat AdB,Sp; Vec diag; ierr = MatCreateVecs(A00,&diag,NULL);CHKERRQ(ierr); if (ainvtype == MAT_SCHUR_COMPLEMENT_AINV_LUMP) { ierr = MatGetRowSum(A00,diag);CHKERRQ(ierr); } else if (ainvtype == MAT_SCHUR_COMPLEMENT_AINV_DIAG) { ierr = MatGetDiagonal(A00,diag);CHKERRQ(ierr); } else SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"Unknown MatSchurComplementAinvType: %D", ainvtype); ierr = VecReciprocal(diag);CHKERRQ(ierr); ierr = MatDuplicate(A01,MAT_COPY_VALUES,&AdB);CHKERRQ(ierr); ierr = MatDiagonalScale(AdB,diag,NULL);CHKERRQ(ierr); ierr = VecDestroy(&diag);CHKERRQ(ierr); Sp = (preuse == MAT_REUSE_MATRIX) ? *Spmat : (Mat)0; ierr = MatMatMult(A10,AdB,preuse,PETSC_DEFAULT,&Sp);CHKERRQ(ierr); if (!A11) { ierr = MatScale(Sp,-1.0);CHKERRQ(ierr); } else { /* TODO: when can we pass SAME_NONZERO_PATTERN? */ ierr = MatAYPX(Sp,-1,A11,DIFFERENT_NONZERO_PATTERN);CHKERRQ(ierr); } *Spmat = Sp; ierr = MatDestroy(&AdB);CHKERRQ(ierr); } PetscFunctionReturn(0); }
// Test Mat BNHat up to N=10 when Op is a diagonal Mat TEST(operatorsCreateBnHeadTest, testBNHatDiagonalOp) { Mat Op; // operator (e.g., Laplacian) PetscReal dt = 2.3, // time-step size c = 0.5, // time-scheme coefficient of the implicit diffusive term val = 2.0 / dt; // value set on the diagonal of the operator PetscInt nx = 10, // number of points in the x-direction ny = 12; // number of points in the y-direction PetscReal ans = nx * ny * dt; // expected sum of all elements of B1Hat // Create and assemble operator MatCreate(PETSC_COMM_WORLD, &Op); MatSetSizes(Op, PETSC_DECIDE, PETSC_DECIDE, nx * ny, nx * ny); MatSetType(Op, MATAIJ); MatSetUp(Op); for (PetscInt i = 0; i < nx * ny; i++) MatSetValues(Op, 1, &i, 1, &i, &val, INSERT_VALUES); MatAssemblyBegin(Op, MAT_FINAL_ASSEMBLY); MatAssemblyEnd(Op, MAT_FINAL_ASSEMBLY); for (PetscInt N = 1; N <= 10; N++) { Mat BNHat; // Nth-order approximation of the inverse of (I/dt - c*Op) // Call function to test petibm::operators::createBnHead(Op, dt, c, N, BNHat); // Check size of Mat BNHat { PetscInt nrows, ncols; MatGetSize(BNHat, &nrows, &ncols); ASSERT_EQ(nx * ny, nrows); ASSERT_EQ(nx * ny, ncols); } // Check sum of elements of BNHat is the expected value if (N > 1) ans += dt * nx * ny * std::pow(c * dt * val, N - 1); { PetscReal sum; Vec v; MatCreateVecs(Op, &v, nullptr); MatGetRowSum(BNHat, v); VecSum(v, &sum); ASSERT_TRUE(std::abs(sum - ans) <= 1.0E-11); VecDestroy(&v); } MatDestroy(&BNHat); } MatDestroy(&Op); }
static PetscErrorCode PCSetUp_Jacobi(PC pc) { PC_Jacobi *jac = (PC_Jacobi*)pc->data; Vec diag,diagsqrt; PetscErrorCode ierr; PetscInt n,i; PetscScalar *x; PetscBool zeroflag = PETSC_FALSE; PetscFunctionBegin; /* For most preconditioners the code would begin here something like if (pc->setupcalled == 0) { allocate space the first time this is ever called ierr = MatCreateVecs(pc->mat,&jac->diag);CHKERRQ(ierr); PetscLogObjectParent((PetscObject)pc,(PetscObject)jac->diag); } But for this preconditioner we want to support use of both the matrix' diagonal elements (for left or right preconditioning) and square root of diagonal elements (for symmetric preconditioning). Hence we do not allocate space here, since we don't know at this point which will be needed (diag and/or diagsqrt) until the user applies the preconditioner, and we don't want to allocate BOTH unless we need them both. Thus, the diag and diagsqrt are allocated in PCSetUp_Jacobi_NonSymmetric() and PCSetUp_Jacobi_Symmetric(), respectively. */ /* Here we set up the preconditioner; that is, we copy the diagonal values from the matrix and put them into a format to make them quick to apply as a preconditioner. */ diag = jac->diag; diagsqrt = jac->diagsqrt; if (diag) { if (jac->userowmax) { ierr = MatGetRowMaxAbs(pc->pmat,diag,NULL);CHKERRQ(ierr); } else if (jac->userowsum) { ierr = MatGetRowSum(pc->pmat,diag);CHKERRQ(ierr); } else { ierr = MatGetDiagonal(pc->pmat,diag);CHKERRQ(ierr); } ierr = VecReciprocal(diag);CHKERRQ(ierr); ierr = VecGetLocalSize(diag,&n);CHKERRQ(ierr); ierr = VecGetArray(diag,&x);CHKERRQ(ierr); if (jac->useabs) { for (i=0; i<n; i++) x[i] = PetscAbsScalar(x[i]); } for (i=0; i<n; i++) { if (x[i] == 0.0) { x[i] = 1.0; zeroflag = PETSC_TRUE; } } ierr = VecRestoreArray(diag,&x);CHKERRQ(ierr); } if (diagsqrt) { if (jac->userowmax) { ierr = MatGetRowMaxAbs(pc->pmat,diagsqrt,NULL);CHKERRQ(ierr); } else if (jac->userowsum) { ierr = MatGetRowSum(pc->pmat,diagsqrt);CHKERRQ(ierr); } else { ierr = MatGetDiagonal(pc->pmat,diagsqrt);CHKERRQ(ierr); } ierr = VecGetLocalSize(diagsqrt,&n);CHKERRQ(ierr); ierr = VecGetArray(diagsqrt,&x);CHKERRQ(ierr); for (i=0; i<n; i++) { if (x[i] != 0.0) x[i] = 1.0/PetscSqrtReal(PetscAbsScalar(x[i])); else { x[i] = 1.0; zeroflag = PETSC_TRUE; } } ierr = VecRestoreArray(diagsqrt,&x);CHKERRQ(ierr); } if (zeroflag) { ierr = PetscInfo(pc,"Zero detected in diagonal of matrix, using 1 at those locations\n");CHKERRQ(ierr); } PetscFunctionReturn(0); }
EXTERN_C_END /* -------------------------------------------------------------------------- */ /* PCSetUp_Jacobi - Prepares for the use of the Jacobi preconditioner by setting data structures and options. Input Parameter: . pc - the preconditioner context Application Interface Routine: PCSetUp() Notes: The interface routine PCSetUp() is not usually called directly by the user, but instead is called by PCApply() if necessary. */ #undef __FUNCT__ #define __FUNCT__ "PCSetUp_Jacobi" static PetscErrorCode PCSetUp_Jacobi(PC pc) { PC_Jacobi *jac = (PC_Jacobi*)pc->data; Vec diag,diagsqrt; PetscErrorCode ierr; PetscInt n,i; PetscScalar *x; PetscBool zeroflag = PETSC_FALSE; PetscFunctionBegin; /* For most preconditioners the code would begin here something like if (pc->setupcalled == 0) { allocate space the first time this is ever called ierr = MatGetVecs(pc->mat,&jac->diag);CHKERRQ(ierr); PetscLogObjectParent(pc,jac->diag); } But for this preconditioner we want to support use of both the matrix' diagonal elements (for left or right preconditioning) and square root of diagonal elements (for symmetric preconditioning). Hence we do not allocate space here, since we don't know at this point which will be needed (diag and/or diagsqrt) until the user applies the preconditioner, and we don't want to allocate BOTH unless we need them both. Thus, the diag and diagsqrt are allocated in PCSetUp_Jacobi_NonSymmetric() and PCSetUp_Jacobi_Symmetric(), respectively. */ /* Here we set up the preconditioner; that is, we copy the diagonal values from the matrix and put them into a format to make them quick to apply as a preconditioner. */ diag = jac->diag; diagsqrt = jac->diagsqrt; if (diag) { if (jac->userowmax) { ierr = MatGetRowMaxAbs(pc->pmat,diag,PETSC_NULL);CHKERRQ(ierr); } else if (jac->userowsum) { ierr = MatGetRowSum(pc->pmat,diag);CHKERRQ(ierr); } else { ierr = MatGetDiagonal(pc->pmat,diag);CHKERRQ(ierr); } ierr = VecReciprocal(diag);CHKERRQ(ierr); ierr = VecGetLocalSize(diag,&n);CHKERRQ(ierr); ierr = VecGetArray(diag,&x);CHKERRQ(ierr); if (jac->useabs) { for (i=0; i<n; i++) { x[i] = PetscAbsScalar(x[i]); } } for (i=0; i<n; i++) { if (x[i] == 0.0) { x[i] = 1.0; zeroflag = PETSC_TRUE; } } ierr = VecRestoreArray(diag,&x);CHKERRQ(ierr); } if (diagsqrt) { if (jac->userowmax) { ierr = MatGetRowMaxAbs(pc->pmat,diagsqrt,PETSC_NULL);CHKERRQ(ierr); } else if (jac->userowsum) { ierr = MatGetRowSum(pc->pmat,diagsqrt);CHKERRQ(ierr); } else { ierr = MatGetDiagonal(pc->pmat,diagsqrt);CHKERRQ(ierr); } ierr = VecGetLocalSize(diagsqrt,&n);CHKERRQ(ierr); ierr = VecGetArray(diagsqrt,&x);CHKERRQ(ierr); for (i=0; i<n; i++) { if (x[i] != 0.0) x[i] = 1.0/PetscSqrtReal(PetscAbsScalar(x[i])); else { x[i] = 1.0; zeroflag = PETSC_TRUE; } } ierr = VecRestoreArray(diagsqrt,&x);CHKERRQ(ierr); } if (zeroflag) { ierr = PetscInfo(pc,"Zero detected in diagonal of matrix, using 1 at those locations\n");CHKERRQ(ierr); } PetscFunctionReturn(0); }