PETSc solving linear system with ksp guide

前端 未结 1 1143
长情又很酷
长情又很酷 2021-02-09 17:02

I am starting use PETSc library to solve linear system of equations in parallel. I have installed all packages, build and run successfully the examples in petsc/src/ksp/ksp/exam

相关标签:
1条回答
  • 2021-02-09 17:37

    Yeah, this can be a little daunting when you're getting started. There's a good walk-through of the process in this ACTS tutorial from 2006; the tutorials listed on the PetSC web page are generally quite good.

    The key parts of this are:

      ierr = MatCreate(PETSC_COMM_WORLD,&A);CHKERRQ(ierr);
    

    Actually create the PetSC matrix object, Mat A;

      ierr = MatSetSizes(A,PETSC_DECIDE,PETSC_DECIDE,m*n,m*n);CHKERRQ(ierr);
    

    set the sizes; here, the matrix is m*n x m*n, as it's a stencil for operating on an m x n 2d grid

      ierr = MatSetFromOptions(A);CHKERRQ(ierr);
    

    This just takes any PetSC command line options that you might have supplied at run time and apply them to the matrix, if you wanted to control how A was set up; otherwise, you could just, have eg, used MatCreateMPIAIJ() to create it as an AIJ-format matrix (the default), MatCreateMPIDense() if it was going to be a dense matrix.

      ierr = MatMPIAIJSetPreallocation(A,5,PETSC_NULL,5,PETSC_NULL);CHKERRQ(ierr);
      ierr = MatSeqAIJSetPreallocation(A,5,PETSC_NULL);CHKERRQ(ierr);
    

    Now that we've gotten an AIJ matrix, these calls just pre-allocates the sparse matrix, assuming 5 non-zeros per row. This is for performance. Note that both the MPI and Seq functions must be called to make sure this works with both 1 processor and multiple processors; this always seemed weird to be, but there you go.

    Ok, now that the matrix is all set up, here's where we start getting into the actual meat of the matter.

    First, we find out which rows this particular process owns. The distribution is by rows, which is a good distribution for typical sparse matrices.

      ierr = MatGetOwnershipRange(A,&Istart,&Iend);CHKERRQ(ierr);
    

    So after this call, each processor has its own version of Istart and Iend, and its this processors job to update rows starting at Istart end ending just before Iend, as you see in this for loop:

      for (Ii=Istart; Ii<Iend; Ii++) { 
        v = -1.0; i = Ii/n; j = Ii - i*n;  
    

    Ok, so if we're operating on row Ii, this corresponds to grid location (i,j) where i = Ii/n and j = Ii % n. Eg, grid location (i,j) corresponds to row Ii = i*n + j. Makes sense?

    I'm going to strip out the if statements here because they're important but they're just dealing with the boundary values and they make things more complicated.

    In this row, there will be a +4 on the diagonal, and -1s at columns corresponding to (i-1,j), (i+1,j), (i,j-1), and (i,j+1). Assuming that we haven't gone off the end of the grid for these (eg, 1 < i < m-1 and 1 < j < n-1), that means

        J = Ii - n; ierr = MatSetValues(A,1,&Ii,1,&J,&v,INSERT_VALUES);CHKERRQ(ierr);
        J = Ii + n; ierr = MatSetValues(A,1,&Ii,1,&J,&v,INSERT_VALUES);CHKERRQ(ierr);
        J = Ii - 1; ierr = MatSetValues(A,1,&Ii,1,&J,&v,INSERT_VALUES);CHKERRQ(ierr);
        J = Ii + 1; ierr = MatSetValues(A,1,&Ii,1,&J,&v,INSERT_VALUES);CHKERRQ(ierr);
    
        v = 4.0; ierr = MatSetValues(A,1,&Ii,1,&Ii,&v,INSERT_VALUES);CHKERRQ(ierr);
      }
    

    The if statements I took out just avoid setting those values if they don't exist, and the CHKERRQ macro just prints out a useful error if ierr != 0, eg the set values call failed (because we tried to set an invalid value).

    Now we've set local values; the MatAssembly calls start communication to ensure any necessary values are exchanged between processors. If you have any unrelated work to do, it can be stuck between the Begin and End to try to overlap communication and computation:

      ierr = MatAssemblyBegin(A,MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
      ierr = MatAssemblyEnd(A,MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
    

    And now you're done and can call your solvers.

    So a typical workflow is:

    • Create your matrix (MatCreate)
    • Set its size (MatSetSizes)
    • Set various matrix options (MatSetFromOptions is a good choice, rather than hardcoding things)
    • For sparse matrices, set the preallocation to reasonable guesses for the number of non-zeros per row; you can do this with a single value (as here), or with an array representing the number of non-zeros per row (here filled in with PETSC_NULL): (MatMPIAIJSetPreallocation, MatSeqAIJSetPreallocation)
    • Find out which rows are your responsibility: (MatGetOwnershipRange)
    • Set the values (calling MatSetValues either once per value, or passing in a chunk of values; INSERT_VALUES sets new elements, ADD_VALUES increments any existing elements)
    • Then do the assembly (MatAssemblyBegin,MatAssemblyEnd).

    Other more complicated use cases are possible.

    0 讨论(0)
提交回复
热议问题