xref: /petsc/src/ksp/pc/impls/mpi/pcmpi.c (revision dd8e379b23d2ef935d8131fb74f7eb73fef09263)
1f1f2ae84SBarry Smith /*
2f1f2ae84SBarry Smith     This file creates an MPI parallel KSP from a sequential PC that lives on MPI rank 0.
3f1f2ae84SBarry Smith     It is intended to allow using PETSc MPI parallel linear solvers from non-MPI codes.
4f1f2ae84SBarry Smith 
5*dd8e379bSPierre Jolivet     That program may use OpenMP to compute the right-hand side and matrix for the linear system
6f1f2ae84SBarry Smith 
7f1f2ae84SBarry Smith     The code uses MPI_COMM_WORLD below but maybe it should be PETSC_COMM_WORLD
8f1f2ae84SBarry Smith 
9f1f2ae84SBarry Smith     The resulting KSP and PC can only be controlled via the options database, though some common commands
10f1f2ae84SBarry Smith     could be passed through the server.
11f1f2ae84SBarry Smith 
12f1f2ae84SBarry Smith */
13f1f2ae84SBarry Smith #include <petsc/private/pcimpl.h>
14f1f2ae84SBarry Smith #include <petsc/private/kspimpl.h>
155e1a0e3cSBarry Smith #include <petsc.h>
16f1f2ae84SBarry Smith 
17f1f2ae84SBarry Smith #define PC_MPI_MAX_RANKS  256
18f1f2ae84SBarry Smith #define PC_MPI_COMM_WORLD MPI_COMM_WORLD
19f1f2ae84SBarry Smith 
20f1f2ae84SBarry Smith typedef struct {
21f1f2ae84SBarry Smith   KSP         ksps[PC_MPI_MAX_RANKS];                               /* The addresses of the MPI parallel KSP on each rank, NULL when not on a rank. */
22f1f2ae84SBarry Smith   PetscMPIInt sendcount[PC_MPI_MAX_RANKS], displ[PC_MPI_MAX_RANKS]; /* For scatter/gather of rhs/solution */
23f1f2ae84SBarry Smith   PetscMPIInt NZ[PC_MPI_MAX_RANKS], NZdispl[PC_MPI_MAX_RANKS];      /* For scatter of nonzero values in matrix (and nonzero column indices initially */
24f1f2ae84SBarry Smith   PetscInt    mincntperrank;                                        /* minimum number of desired nonzeros per active rank in MPI parallel KSP solve */
25f1f2ae84SBarry Smith   PetscBool   alwaysuseserver;                                      /* for debugging use the server infrastructure even if only one MPI rank is used for the solve */
26f1f2ae84SBarry Smith } PC_MPI;
27f1f2ae84SBarry Smith 
289371c9d4SSatish Balay typedef enum {
299371c9d4SSatish Balay   PCMPI_EXIT, /* exit the PC server loop, means the controlling sequential program is done */
30f1f2ae84SBarry Smith   PCMPI_CREATE,
31f1f2ae84SBarry Smith   PCMPI_SET_MAT,           /* set original matrix (or one with different nonzero pattern) */
32f1f2ae84SBarry Smith   PCMPI_UPDATE_MAT_VALUES, /* update current matrix with new nonzero values */
33f1f2ae84SBarry Smith   PCMPI_SOLVE,
34f1f2ae84SBarry Smith   PCMPI_VIEW,
35f1f2ae84SBarry Smith   PCMPI_DESTROY /* destroy a KSP that is no longer needed */
36f1f2ae84SBarry Smith } PCMPICommand;
37f1f2ae84SBarry Smith 
38f1f2ae84SBarry Smith static MPI_Comm  PCMPIComms[PC_MPI_MAX_RANKS];
39f1f2ae84SBarry Smith static PetscBool PCMPICommSet = PETSC_FALSE;
40f1f2ae84SBarry Smith static PetscInt  PCMPISolveCounts[PC_MPI_MAX_RANKS], PCMPIKSPCounts[PC_MPI_MAX_RANKS], PCMPIMatCounts[PC_MPI_MAX_RANKS], PCMPISolveCountsSeq = 0, PCMPIKSPCountsSeq = 0;
415316cbedSBarry Smith static PetscInt  PCMPIIterations[PC_MPI_MAX_RANKS], PCMPISizes[PC_MPI_MAX_RANKS], PCMPIIterationsSeq = 0, PCMPISizesSeq = 0;
42f1f2ae84SBarry Smith 
43d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCMPICommsCreate(void)
44d71ae5a4SJacob Faibussowitsch {
45f1f2ae84SBarry Smith   MPI_Comm    comm = PC_MPI_COMM_WORLD;
46f1f2ae84SBarry Smith   PetscMPIInt size, rank, i;
47f1f2ae84SBarry Smith 
48f1f2ae84SBarry Smith   PetscFunctionBegin;
49f1f2ae84SBarry Smith   PetscCallMPI(MPI_Comm_size(comm, &size));
50f1f2ae84SBarry Smith   PetscCheck(size <= PC_MPI_MAX_RANKS, PETSC_COMM_SELF, PETSC_ERR_SUP, "No support for using more than PC_MPI_MAX_RANKS MPI ranks in an MPI linear solver server solve");
51f1f2ae84SBarry Smith   PetscCallMPI(MPI_Comm_rank(comm, &rank));
52f1f2ae84SBarry Smith   /* comm for size 1 is useful only for debugging */
53f1f2ae84SBarry Smith   for (i = 0; i < size; i++) {
54f1f2ae84SBarry Smith     PetscMPIInt color = rank < i + 1 ? 0 : MPI_UNDEFINED;
55f1f2ae84SBarry Smith     PetscCallMPI(MPI_Comm_split(comm, color, 0, &PCMPIComms[i]));
56f1f2ae84SBarry Smith     PCMPISolveCounts[i] = 0;
57f1f2ae84SBarry Smith     PCMPIKSPCounts[i]   = 0;
585316cbedSBarry Smith     PCMPIIterations[i]  = 0;
595316cbedSBarry Smith     PCMPISizes[i]       = 0;
60f1f2ae84SBarry Smith   }
61f1f2ae84SBarry Smith   PCMPICommSet = PETSC_TRUE;
623ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
63f1f2ae84SBarry Smith }
64f1f2ae84SBarry Smith 
65d71ae5a4SJacob Faibussowitsch PetscErrorCode PCMPICommsDestroy(void)
66d71ae5a4SJacob Faibussowitsch {
67f1f2ae84SBarry Smith   MPI_Comm    comm = PC_MPI_COMM_WORLD;
68f1f2ae84SBarry Smith   PetscMPIInt size, rank, i;
69f1f2ae84SBarry Smith 
70f1f2ae84SBarry Smith   PetscFunctionBegin;
713ba16761SJacob Faibussowitsch   if (!PCMPICommSet) PetscFunctionReturn(PETSC_SUCCESS);
72f1f2ae84SBarry Smith   PetscCallMPI(MPI_Comm_size(comm, &size));
73f1f2ae84SBarry Smith   PetscCallMPI(MPI_Comm_rank(comm, &rank));
74f1f2ae84SBarry Smith   for (i = 0; i < size; i++) {
75f1f2ae84SBarry Smith     if (PCMPIComms[i] != MPI_COMM_NULL) PetscCallMPI(MPI_Comm_free(&PCMPIComms[i]));
76f1f2ae84SBarry Smith   }
77f1f2ae84SBarry Smith   PCMPICommSet = PETSC_FALSE;
783ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
79f1f2ae84SBarry Smith }
80f1f2ae84SBarry Smith 
81d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCMPICreate(PC pc)
82d71ae5a4SJacob Faibussowitsch {
83f1f2ae84SBarry Smith   PC_MPI     *km   = pc ? (PC_MPI *)pc->data : NULL;
84f1f2ae84SBarry Smith   MPI_Comm    comm = PC_MPI_COMM_WORLD;
85f1f2ae84SBarry Smith   KSP         ksp;
86f1f2ae84SBarry Smith   PetscInt    N[2], mincntperrank = 0;
87f1f2ae84SBarry Smith   PetscMPIInt size;
88f1f2ae84SBarry Smith   Mat         sA;
893821be0aSBarry Smith   char       *cprefix = NULL;
90f1f2ae84SBarry Smith   PetscMPIInt len     = 0;
91f1f2ae84SBarry Smith 
92f1f2ae84SBarry Smith   PetscFunctionBegin;
93f1f2ae84SBarry Smith   if (!PCMPICommSet) PetscCall(PCMPICommsCreate());
94f1f2ae84SBarry Smith   PetscCallMPI(MPI_Comm_size(comm, &size));
95f1f2ae84SBarry Smith   if (pc) {
96f1f2ae84SBarry Smith     if (size == 1) PetscCall(PetscPrintf(PETSC_COMM_SELF, "Warning: Running KSP type of MPI on a one rank MPI run, this will be less efficient then not using this type\n"));
97f1f2ae84SBarry Smith     PetscCall(PCGetOperators(pc, &sA, &sA));
98f1f2ae84SBarry Smith     PetscCall(MatGetSize(sA, &N[0], &N[1]));
99f1f2ae84SBarry Smith   }
100f1f2ae84SBarry Smith   PetscCallMPI(MPI_Bcast(N, 2, MPIU_INT, 0, comm));
101f1f2ae84SBarry Smith 
102f1f2ae84SBarry Smith   /* choose a suitable sized MPI_Comm for the problem to be solved on */
103f1f2ae84SBarry Smith   if (km) mincntperrank = km->mincntperrank;
104f1f2ae84SBarry Smith   PetscCallMPI(MPI_Bcast(&mincntperrank, 1, MPI_INT, 0, comm));
105f1f2ae84SBarry Smith   comm = PCMPIComms[PetscMin(size, PetscMax(1, N[0] / mincntperrank)) - 1];
106f1f2ae84SBarry Smith   if (comm == MPI_COMM_NULL) {
107f1f2ae84SBarry Smith     ksp = NULL;
1083ba16761SJacob Faibussowitsch     PetscFunctionReturn(PETSC_SUCCESS);
109f1f2ae84SBarry Smith   }
1100316ec64SBarry Smith   PetscCall(PetscLogStagePush(PCMPIStage));
111f1f2ae84SBarry Smith   PetscCall(KSPCreate(comm, &ksp));
1123821be0aSBarry Smith   PetscCall(KSPSetNestLevel(ksp, 1));
1133821be0aSBarry Smith   PetscCall(PetscObjectSetTabLevel((PetscObject)ksp, 1));
1140316ec64SBarry Smith   PetscCall(PetscLogStagePop());
115f1f2ae84SBarry Smith   PetscCallMPI(MPI_Gather(&ksp, 1, MPI_AINT, pc ? km->ksps : NULL, 1, MPI_AINT, 0, comm));
116f1f2ae84SBarry Smith   if (pc) {
117f1f2ae84SBarry Smith     size_t      slen;
1183821be0aSBarry Smith     const char *prefix = NULL;
119f9818f3cSJose E. Roman     char       *found  = NULL;
120f1f2ae84SBarry Smith 
121f1f2ae84SBarry Smith     PetscCallMPI(MPI_Comm_size(comm, &size));
122dad3da8eSBarry Smith     PCMPIKSPCounts[size - 1]++;
1233821be0aSBarry Smith     /* Created KSP gets prefix of PC minus the mpi_linear_solver_server_ portion */
1243821be0aSBarry Smith     PetscCall(PCGetOptionsPrefix(pc, &prefix));
1253821be0aSBarry Smith     PetscCheck(prefix, PETSC_COMM_SELF, PETSC_ERR_PLIB, "PCMPI missing required prefix");
1263821be0aSBarry Smith     PetscCall(PetscStrallocpy(prefix, &cprefix));
1273821be0aSBarry Smith     PetscCall(PetscStrstr(cprefix, "mpi_linear_solver_server_", &found));
1283821be0aSBarry Smith     PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_PLIB, "PCMPI missing mpi_linear_solver_server_ portion of prefix");
1293821be0aSBarry Smith     *found = 0;
1303821be0aSBarry Smith     PetscCall(PetscStrlen(cprefix, &slen));
131f1f2ae84SBarry Smith     len = (PetscMPIInt)slen;
132f1f2ae84SBarry Smith   }
133f1f2ae84SBarry Smith   PetscCallMPI(MPI_Bcast(&len, 1, MPI_INT, 0, comm));
134f1f2ae84SBarry Smith   if (len) {
1353821be0aSBarry Smith     if (!pc) PetscCall(PetscMalloc1(len + 1, &cprefix));
1363821be0aSBarry Smith     PetscCallMPI(MPI_Bcast(cprefix, len + 1, MPI_CHAR, 0, comm));
1373821be0aSBarry Smith     PetscCall(KSPSetOptionsPrefix(ksp, cprefix));
138f1f2ae84SBarry Smith   }
1393821be0aSBarry Smith   PetscCall(PetscFree(cprefix));
1403ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
141f1f2ae84SBarry Smith }
142f1f2ae84SBarry Smith 
143d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCMPISetMat(PC pc)
144d71ae5a4SJacob Faibussowitsch {
145f1f2ae84SBarry Smith   PC_MPI            *km = pc ? (PC_MPI *)pc->data : NULL;
146f1f2ae84SBarry Smith   Mat                A;
14768a21331SBarry Smith   PetscInt           m, n, *ia, *ja, j, bs;
148f1f2ae84SBarry Smith   Mat                sA;
149f1f2ae84SBarry Smith   MPI_Comm           comm = PC_MPI_COMM_WORLD;
150f1f2ae84SBarry Smith   KSP                ksp;
151f1f2ae84SBarry Smith   PetscLayout        layout;
152f1f2ae84SBarry Smith   const PetscInt    *IA = NULL, *JA = NULL;
153f1f2ae84SBarry Smith   const PetscInt    *range;
154f1f2ae84SBarry Smith   PetscMPIInt       *NZ = NULL, sendcounti[PC_MPI_MAX_RANKS], displi[PC_MPI_MAX_RANKS], *NZdispl = NULL, nz, size, i;
155f1f2ae84SBarry Smith   PetscScalar       *a;
156f1f2ae84SBarry Smith   const PetscScalar *sa               = NULL;
1573821be0aSBarry Smith   PetscInt           matproperties[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1583821be0aSBarry Smith   char              *cprefix;
159f1f2ae84SBarry Smith 
160f1f2ae84SBarry Smith   PetscFunctionBegin;
161f1f2ae84SBarry Smith   PetscCallMPI(MPI_Scatter(pc ? km->ksps : NULL, 1, MPI_AINT, &ksp, 1, MPI_AINT, 0, comm));
1623ba16761SJacob Faibussowitsch   if (!ksp) PetscFunctionReturn(PETSC_SUCCESS);
163f1f2ae84SBarry Smith   PetscCall(PetscObjectGetComm((PetscObject)ksp, &comm));
164f1f2ae84SBarry Smith   if (pc) {
16568a21331SBarry Smith     PetscBool   isset, issymmetric, ishermitian, isspd, isstructurallysymmetric;
1663821be0aSBarry Smith     const char *prefix;
1673821be0aSBarry Smith     size_t      clen;
16868a21331SBarry Smith 
169f1f2ae84SBarry Smith     PetscCallMPI(MPI_Comm_size(comm, &size));
170dad3da8eSBarry Smith     PCMPIMatCounts[size - 1]++;
171f1f2ae84SBarry Smith     PetscCall(PCGetOperators(pc, &sA, &sA));
17268a21331SBarry Smith     PetscCall(MatGetSize(sA, &matproperties[0], &matproperties[1]));
173dd0d27b1SBarry Smith     PetscCall(MatGetBlockSize(sA, &bs));
17468a21331SBarry Smith     matproperties[2] = bs;
17568a21331SBarry Smith     PetscCall(MatIsSymmetricKnown(sA, &isset, &issymmetric));
17668a21331SBarry Smith     matproperties[3] = !isset ? 0 : (issymmetric ? 1 : 2);
17768a21331SBarry Smith     PetscCall(MatIsHermitianKnown(sA, &isset, &ishermitian));
17868a21331SBarry Smith     matproperties[4] = !isset ? 0 : (ishermitian ? 1 : 2);
17968a21331SBarry Smith     PetscCall(MatIsSPDKnown(sA, &isset, &isspd));
18068a21331SBarry Smith     matproperties[5] = !isset ? 0 : (isspd ? 1 : 2);
18168a21331SBarry Smith     PetscCall(MatIsStructurallySymmetricKnown(sA, &isset, &isstructurallysymmetric));
18268a21331SBarry Smith     matproperties[6] = !isset ? 0 : (isstructurallysymmetric ? 1 : 2);
1837a99bfcaSBarry Smith     /* Created Mat gets prefix of input Mat PLUS the mpi_linear_solver_server_ portion */
1843821be0aSBarry Smith     PetscCall(MatGetOptionsPrefix(sA, &prefix));
1853821be0aSBarry Smith     PetscCall(PetscStrallocpy(prefix, &cprefix));
1863821be0aSBarry Smith     PetscCall(PetscStrlen(cprefix, &clen));
1873821be0aSBarry Smith     matproperties[7] = (PetscInt)clen;
188f1f2ae84SBarry Smith   }
1893821be0aSBarry Smith   PetscCallMPI(MPI_Bcast(matproperties, PETSC_STATIC_ARRAY_LENGTH(matproperties), MPIU_INT, 0, comm));
190f1f2ae84SBarry Smith 
19168a21331SBarry Smith   /* determine ownership ranges of matrix columns */
192f1f2ae84SBarry Smith   PetscCall(PetscLayoutCreate(comm, &layout));
19368a21331SBarry Smith   PetscCall(PetscLayoutSetBlockSize(layout, matproperties[2]));
19468a21331SBarry Smith   PetscCall(PetscLayoutSetSize(layout, matproperties[1]));
195f1f2ae84SBarry Smith   PetscCall(PetscLayoutSetUp(layout));
196f1f2ae84SBarry Smith   PetscCall(PetscLayoutGetLocalSize(layout, &n));
19768a21331SBarry Smith   PetscCall(PetscLayoutDestroy(&layout));
19868a21331SBarry Smith 
19968a21331SBarry Smith   /* determine ownership ranges of matrix rows */
20068a21331SBarry Smith   PetscCall(PetscLayoutCreate(comm, &layout));
20168a21331SBarry Smith   PetscCall(PetscLayoutSetBlockSize(layout, matproperties[2]));
20268a21331SBarry Smith   PetscCall(PetscLayoutSetSize(layout, matproperties[0]));
20368a21331SBarry Smith   PetscCall(PetscLayoutSetUp(layout));
20468a21331SBarry Smith   PetscCall(PetscLayoutGetLocalSize(layout, &m));
205f1f2ae84SBarry Smith 
206f1f2ae84SBarry Smith   /* copy over the matrix nonzero structure and values */
207f1f2ae84SBarry Smith   if (pc) {
208f1f2ae84SBarry Smith     NZ      = km->NZ;
209f1f2ae84SBarry Smith     NZdispl = km->NZdispl;
210f1f2ae84SBarry Smith     PetscCall(PetscLayoutGetRanges(layout, &range));
211f1f2ae84SBarry Smith     PetscCall(MatGetRowIJ(sA, 0, PETSC_FALSE, PETSC_FALSE, NULL, &IA, &JA, NULL));
212f1f2ae84SBarry Smith     for (i = 0; i < size; i++) {
213f1f2ae84SBarry Smith       sendcounti[i] = (PetscMPIInt)(1 + range[i + 1] - range[i]);
214f1f2ae84SBarry Smith       NZ[i]         = (PetscMPIInt)(IA[range[i + 1]] - IA[range[i]]);
215f1f2ae84SBarry Smith     }
216f1f2ae84SBarry Smith     displi[0]  = 0;
217f1f2ae84SBarry Smith     NZdispl[0] = 0;
218f1f2ae84SBarry Smith     for (j = 1; j < size; j++) {
219f1f2ae84SBarry Smith       displi[j]  = displi[j - 1] + sendcounti[j - 1] - 1;
220f1f2ae84SBarry Smith       NZdispl[j] = NZdispl[j - 1] + NZ[j - 1];
221f1f2ae84SBarry Smith     }
222f1f2ae84SBarry Smith     PetscCall(MatSeqAIJGetArrayRead(sA, &sa));
223f1f2ae84SBarry Smith   }
224f1f2ae84SBarry Smith   PetscCall(PetscLayoutDestroy(&layout));
225f1f2ae84SBarry Smith   PetscCallMPI(MPI_Scatter(NZ, 1, MPI_INT, &nz, 1, MPI_INT, 0, comm));
226f1f2ae84SBarry Smith 
227f1f2ae84SBarry Smith   PetscCall(PetscMalloc3(n + 1, &ia, nz, &ja, nz, &a));
228f1f2ae84SBarry Smith   PetscCallMPI(MPI_Scatterv(IA, sendcounti, displi, MPIU_INT, ia, n + 1, MPIU_INT, 0, comm));
229f1f2ae84SBarry Smith   PetscCallMPI(MPI_Scatterv(JA, NZ, NZdispl, MPIU_INT, ja, nz, MPIU_INT, 0, comm));
230f1f2ae84SBarry Smith   PetscCallMPI(MPI_Scatterv(sa, NZ, NZdispl, MPIU_SCALAR, a, nz, MPIU_SCALAR, 0, comm));
231f1f2ae84SBarry Smith 
232f1f2ae84SBarry Smith   if (pc) {
233f1f2ae84SBarry Smith     PetscCall(MatSeqAIJRestoreArrayRead(sA, &sa));
234f1f2ae84SBarry Smith     PetscCall(MatRestoreRowIJ(sA, 0, PETSC_FALSE, PETSC_FALSE, NULL, &IA, &JA, NULL));
235f1f2ae84SBarry Smith   }
236f1f2ae84SBarry Smith 
237ad540459SPierre Jolivet   for (j = 1; j < n + 1; j++) ia[j] -= ia[0];
238f1f2ae84SBarry Smith   ia[0] = 0;
2390316ec64SBarry Smith   PetscCall(PetscLogStagePush(PCMPIStage));
2403821be0aSBarry Smith   PetscCall(MatCreate(comm, &A));
2413821be0aSBarry Smith   if (matproperties[7] > 0) {
2423821be0aSBarry Smith     if (!pc) PetscCall(PetscMalloc1(matproperties[7] + 1, &cprefix));
2433821be0aSBarry Smith     PetscCallMPI(MPI_Bcast(cprefix, matproperties[7] + 1, MPI_CHAR, 0, comm));
2443821be0aSBarry Smith     PetscCall(MatSetOptionsPrefix(A, cprefix));
2453821be0aSBarry Smith     PetscCall(PetscFree(cprefix));
2463821be0aSBarry Smith   }
2473821be0aSBarry Smith   PetscCall(MatAppendOptionsPrefix(A, "mpi_linear_solver_server_"));
2483821be0aSBarry Smith   PetscCall(MatSetSizes(A, m, n, matproperties[0], matproperties[1]));
2493821be0aSBarry Smith   PetscCall(MatSetType(A, MATMPIAIJ));
2503821be0aSBarry Smith   PetscCall(MatMPIAIJSetPreallocationCSR(A, ia, ja, a));
25168a21331SBarry Smith   PetscCall(MatSetBlockSize(A, matproperties[2]));
2523821be0aSBarry Smith 
25368a21331SBarry Smith   if (matproperties[3]) PetscCall(MatSetOption(A, MAT_SYMMETRIC, matproperties[3] == 1 ? PETSC_TRUE : PETSC_FALSE));
25468a21331SBarry Smith   if (matproperties[4]) PetscCall(MatSetOption(A, MAT_HERMITIAN, matproperties[4] == 1 ? PETSC_TRUE : PETSC_FALSE));
25568a21331SBarry Smith   if (matproperties[5]) PetscCall(MatSetOption(A, MAT_SPD, matproperties[5] == 1 ? PETSC_TRUE : PETSC_FALSE));
25668a21331SBarry Smith   if (matproperties[6]) PetscCall(MatSetOption(A, MAT_STRUCTURALLY_SYMMETRIC, matproperties[6] == 1 ? PETSC_TRUE : PETSC_FALSE));
257f1f2ae84SBarry Smith 
258f1f2ae84SBarry Smith   PetscCall(PetscFree3(ia, ja, a));
259f1f2ae84SBarry Smith   PetscCall(KSPSetOperators(ksp, A, A));
260f1f2ae84SBarry Smith   if (!ksp->vec_sol) PetscCall(MatCreateVecs(A, &ksp->vec_sol, &ksp->vec_rhs));
2610316ec64SBarry Smith   PetscCall(PetscLogStagePop());
262f1f2ae84SBarry Smith   if (pc) { /* needed for scatterv/gatherv of rhs and solution */
263f1f2ae84SBarry Smith     const PetscInt *range;
264f1f2ae84SBarry Smith 
265f1f2ae84SBarry Smith     PetscCall(VecGetOwnershipRanges(ksp->vec_sol, &range));
266f1f2ae84SBarry Smith     for (i = 0; i < size; i++) {
267f1f2ae84SBarry Smith       km->sendcount[i] = (PetscMPIInt)(range[i + 1] - range[i]);
268f1f2ae84SBarry Smith       km->displ[i]     = (PetscMPIInt)range[i];
269f1f2ae84SBarry Smith     }
270f1f2ae84SBarry Smith   }
271f1f2ae84SBarry Smith   PetscCall(MatDestroy(&A));
272f1f2ae84SBarry Smith   PetscCall(KSPSetFromOptions(ksp));
2733ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
274f1f2ae84SBarry Smith }
275f1f2ae84SBarry Smith 
276d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCMPIUpdateMatValues(PC pc)
277d71ae5a4SJacob Faibussowitsch {
278f1f2ae84SBarry Smith   PC_MPI            *km = pc ? (PC_MPI *)pc->data : NULL;
279f1f2ae84SBarry Smith   KSP                ksp;
280f1f2ae84SBarry Smith   Mat                sA, A;
281f1f2ae84SBarry Smith   MPI_Comm           comm = PC_MPI_COMM_WORLD;
282f1f2ae84SBarry Smith   PetscScalar       *a;
283f1f2ae84SBarry Smith   PetscCount         nz;
284f1f2ae84SBarry Smith   const PetscScalar *sa = NULL;
285dad3da8eSBarry Smith   PetscMPIInt        size;
28668a21331SBarry Smith   PetscInt           matproperties[4] = {0, 0, 0, 0};
287f1f2ae84SBarry Smith 
288f1f2ae84SBarry Smith   PetscFunctionBegin;
289f1f2ae84SBarry Smith   if (pc) {
290f1f2ae84SBarry Smith     PetscCall(PCGetOperators(pc, &sA, &sA));
291f1f2ae84SBarry Smith     PetscCall(MatSeqAIJGetArrayRead(sA, &sa));
292f1f2ae84SBarry Smith   }
293f1f2ae84SBarry Smith   PetscCallMPI(MPI_Scatter(pc ? km->ksps : NULL, 1, MPI_AINT, &ksp, 1, MPI_AINT, 0, comm));
2943ba16761SJacob Faibussowitsch   if (!ksp) PetscFunctionReturn(PETSC_SUCCESS);
295f1f2ae84SBarry Smith   PetscCall(PetscObjectGetComm((PetscObject)ksp, &comm));
296dad3da8eSBarry Smith   PetscCallMPI(MPI_Comm_size(comm, &size));
297dad3da8eSBarry Smith   PCMPIMatCounts[size - 1]++;
298f1f2ae84SBarry Smith   PetscCall(KSPGetOperators(ksp, NULL, &A));
299f1f2ae84SBarry Smith   PetscCall(MatMPIAIJGetNumberNonzeros(A, &nz));
300f1f2ae84SBarry Smith   PetscCall(PetscMalloc1(nz, &a));
301f1f2ae84SBarry Smith   PetscCallMPI(MPI_Scatterv(sa, pc ? km->NZ : NULL, pc ? km->NZdispl : NULL, MPIU_SCALAR, a, nz, MPIU_SCALAR, 0, comm));
30268a21331SBarry Smith   if (pc) {
30368a21331SBarry Smith     PetscBool isset, issymmetric, ishermitian, isspd, isstructurallysymmetric;
30468a21331SBarry Smith 
30568a21331SBarry Smith     PetscCall(MatSeqAIJRestoreArrayRead(sA, &sa));
30668a21331SBarry Smith 
30768a21331SBarry Smith     PetscCall(MatIsSymmetricKnown(sA, &isset, &issymmetric));
30868a21331SBarry Smith     matproperties[0] = !isset ? 0 : (issymmetric ? 1 : 2);
30968a21331SBarry Smith     PetscCall(MatIsHermitianKnown(sA, &isset, &ishermitian));
31068a21331SBarry Smith     matproperties[1] = !isset ? 0 : (ishermitian ? 1 : 2);
31168a21331SBarry Smith     PetscCall(MatIsSPDKnown(sA, &isset, &isspd));
31268a21331SBarry Smith     matproperties[2] = !isset ? 0 : (isspd ? 1 : 2);
31368a21331SBarry Smith     PetscCall(MatIsStructurallySymmetricKnown(sA, &isset, &isstructurallysymmetric));
31468a21331SBarry Smith     matproperties[3] = !isset ? 0 : (isstructurallysymmetric ? 1 : 2);
31568a21331SBarry Smith   }
316f1f2ae84SBarry Smith   PetscCall(MatUpdateMPIAIJWithArray(A, a));
317f1f2ae84SBarry Smith   PetscCall(PetscFree(a));
31868a21331SBarry Smith   PetscCallMPI(MPI_Bcast(matproperties, 4, MPIU_INT, 0, comm));
31968a21331SBarry Smith   /* if any of these properties was previously set and is now not set this will result in incorrect properties in A since there is no way to unset a property */
32068a21331SBarry Smith   if (matproperties[0]) PetscCall(MatSetOption(A, MAT_SYMMETRIC, matproperties[0] == 1 ? PETSC_TRUE : PETSC_FALSE));
32168a21331SBarry Smith   if (matproperties[1]) PetscCall(MatSetOption(A, MAT_HERMITIAN, matproperties[1] == 1 ? PETSC_TRUE : PETSC_FALSE));
32268a21331SBarry Smith   if (matproperties[2]) PetscCall(MatSetOption(A, MAT_SPD, matproperties[2] == 1 ? PETSC_TRUE : PETSC_FALSE));
32368a21331SBarry Smith   if (matproperties[3]) PetscCall(MatSetOption(A, MAT_STRUCTURALLY_SYMMETRIC, matproperties[3] == 1 ? PETSC_TRUE : PETSC_FALSE));
3243ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
325f1f2ae84SBarry Smith }
326f1f2ae84SBarry Smith 
327d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCMPISolve(PC pc, Vec B, Vec X)
328d71ae5a4SJacob Faibussowitsch {
329f1f2ae84SBarry Smith   PC_MPI            *km = pc ? (PC_MPI *)pc->data : NULL;
330f1f2ae84SBarry Smith   KSP                ksp;
331f1f2ae84SBarry Smith   MPI_Comm           comm = PC_MPI_COMM_WORLD;
332f1f2ae84SBarry Smith   const PetscScalar *sb   = NULL, *x;
333f1f2ae84SBarry Smith   PetscScalar       *b, *sx = NULL;
3345316cbedSBarry Smith   PetscInt           its, n;
3355316cbedSBarry Smith   PetscMPIInt        size;
336f1f2ae84SBarry Smith 
337f1f2ae84SBarry Smith   PetscFunctionBegin;
338f1f2ae84SBarry Smith   PetscCallMPI(MPI_Scatter(pc ? km->ksps : &ksp, 1, MPI_AINT, &ksp, 1, MPI_AINT, 0, comm));
3393ba16761SJacob Faibussowitsch   if (!ksp) PetscFunctionReturn(PETSC_SUCCESS);
340f1f2ae84SBarry Smith   PetscCall(PetscObjectGetComm((PetscObject)ksp, &comm));
341f1f2ae84SBarry Smith 
342f1f2ae84SBarry Smith   /* TODO: optimize code to not require building counts/displ every time */
343f1f2ae84SBarry Smith 
344f1f2ae84SBarry Smith   /* scatterv rhs */
345dad3da8eSBarry Smith   PetscCallMPI(MPI_Comm_size(comm, &size));
3465316cbedSBarry Smith   if (pc) {
3475316cbedSBarry Smith     PetscInt N;
3485316cbedSBarry Smith 
349dad3da8eSBarry Smith     PCMPISolveCounts[size - 1]++;
35068a21331SBarry Smith     PetscCall(MatGetSize(pc->pmat, &N, NULL));
3515316cbedSBarry Smith     PCMPISizes[size - 1] += N;
352f1f2ae84SBarry Smith     PetscCall(VecGetArrayRead(B, &sb));
353f1f2ae84SBarry Smith   }
354f1f2ae84SBarry Smith   PetscCall(VecGetLocalSize(ksp->vec_rhs, &n));
355f1f2ae84SBarry Smith   PetscCall(VecGetArray(ksp->vec_rhs, &b));
356f1f2ae84SBarry Smith   PetscCallMPI(MPI_Scatterv(sb, pc ? km->sendcount : NULL, pc ? km->displ : NULL, MPIU_SCALAR, b, n, MPIU_SCALAR, 0, comm));
357f1f2ae84SBarry Smith   PetscCall(VecRestoreArray(ksp->vec_rhs, &b));
358f1f2ae84SBarry Smith   if (pc) PetscCall(VecRestoreArrayRead(B, &sb));
359f1f2ae84SBarry Smith 
3600316ec64SBarry Smith   PetscCall(PetscLogStagePush(PCMPIStage));
361f1f2ae84SBarry Smith   PetscCall(KSPSolve(ksp, NULL, NULL));
3620316ec64SBarry Smith   PetscCall(PetscLogStagePop());
3635316cbedSBarry Smith   PetscCall(KSPGetIterationNumber(ksp, &its));
3645316cbedSBarry Smith   PCMPIIterations[size - 1] += its;
365f1f2ae84SBarry Smith 
366f1f2ae84SBarry Smith   /* gather solution */
367f1f2ae84SBarry Smith   PetscCall(VecGetArrayRead(ksp->vec_sol, &x));
368f1f2ae84SBarry Smith   if (pc) PetscCall(VecGetArray(X, &sx));
369f1f2ae84SBarry Smith   PetscCallMPI(MPI_Gatherv(x, n, MPIU_SCALAR, sx, pc ? km->sendcount : NULL, pc ? km->displ : NULL, MPIU_SCALAR, 0, comm));
370f1f2ae84SBarry Smith   if (pc) PetscCall(VecRestoreArray(X, &sx));
371f1f2ae84SBarry Smith   PetscCall(VecRestoreArrayRead(ksp->vec_sol, &x));
3723ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
373f1f2ae84SBarry Smith }
374f1f2ae84SBarry Smith 
375d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCMPIDestroy(PC pc)
376d71ae5a4SJacob Faibussowitsch {
377f1f2ae84SBarry Smith   PC_MPI  *km = pc ? (PC_MPI *)pc->data : NULL;
378f1f2ae84SBarry Smith   KSP      ksp;
379f1f2ae84SBarry Smith   MPI_Comm comm = PC_MPI_COMM_WORLD;
380f1f2ae84SBarry Smith 
381f1f2ae84SBarry Smith   PetscFunctionBegin;
382f1f2ae84SBarry Smith   PetscCallMPI(MPI_Scatter(pc ? km->ksps : NULL, 1, MPI_AINT, &ksp, 1, MPI_AINT, 0, comm));
3833ba16761SJacob Faibussowitsch   if (!ksp) PetscFunctionReturn(PETSC_SUCCESS);
384c7d372c4SBarry Smith   PetscCall(PetscLogStagePush(PCMPIStage));
385f1f2ae84SBarry Smith   PetscCall(KSPDestroy(&ksp));
386c7d372c4SBarry Smith   PetscCall(PetscLogStagePop());
3873ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
388f1f2ae84SBarry Smith }
389f1f2ae84SBarry Smith 
3903821be0aSBarry Smith PetscBool PCMPIServerActive = PETSC_FALSE;
3913821be0aSBarry Smith 
392f1f2ae84SBarry Smith /*@C
3937a99bfcaSBarry Smith   PCMPIServerBegin - starts a server that runs on the `rank != 0` MPI processes waiting to process requests for
394f1580f4eSBarry Smith   parallel `KSP` solves and management of parallel `KSP` objects.
395f1f2ae84SBarry Smith 
3963821be0aSBarry Smith   Logically Collective on all MPI processes except rank 0
397f1f2ae84SBarry Smith 
398f1580f4eSBarry Smith   Options Database Keys:
399f1f2ae84SBarry Smith + -mpi_linear_solver_server      - causes the PETSc program to start in MPI linear solver server mode where only the first MPI rank runs user code
4003821be0aSBarry Smith - -mpi_linear_solver_server_view - displays information about all the linear systems solved by the MPI linear solver server at the conclusion of the program
401f1f2ae84SBarry Smith 
40220f4b53cSBarry Smith   Level: developer
40320f4b53cSBarry Smith 
404f1580f4eSBarry Smith   Note:
405f1f2ae84SBarry Smith   This is normally started automatically in `PetscInitialize()` when the option is provided
406f1f2ae84SBarry Smith 
4073821be0aSBarry Smith   See `PCMPI` for information on using the solver with a `KSP` object
4083821be0aSBarry Smith 
409f1f2ae84SBarry Smith   Developer Notes:
4103821be0aSBarry Smith   When called on MPI rank 0 this sets `PETSC_COMM_WORLD` to `PETSC_COMM_SELF` to allow a main program
411f1580f4eSBarry Smith   written with `PETSC_COMM_WORLD` to run correctly on the single rank while all the ranks
412f1580f4eSBarry Smith   (that would normally be sharing `PETSC_COMM_WORLD`) to run the solver server.
413f1f2ae84SBarry Smith 
414f1580f4eSBarry Smith   Can this be integrated into the `PetscDevice` abstraction that is currently being developed?
415f1f2ae84SBarry Smith 
4163821be0aSBarry Smith   Conceivably `PCREDISTRIBUTE` could be organized in a similar manner to simplify its usage
4173821be0aSBarry Smith 
4183821be0aSBarry Smith   This could be implemented directly at the `KSP` level instead of using the `PCMPI` wrapper object
4193821be0aSBarry Smith 
420baca6076SPierre Jolivet   The code could be extended to allow an MPI + OpenMP application to use the linear solver server concept across all shared-memory
4213821be0aSBarry Smith   nodes with a single MPI process per node for the user application but multiple MPI processes per node for the linear solver.
4223821be0aSBarry Smith 
4237a99bfcaSBarry Smith   The concept could also be extended for users's callbacks for `SNES`, `TS`, and `Tao` where the `SNESSolve()` for example, runs on
4243821be0aSBarry Smith   all MPI processes but the user callback only runs on one MPI process per node.
4253821be0aSBarry Smith 
4263821be0aSBarry Smith   PETSc could also be extended with an MPI-less API that provides access to PETSc's solvers without any reference to MPI, essentially remove
4273821be0aSBarry Smith   the `MPI_Comm` argument from PETSc calls.
4283821be0aSBarry Smith 
4293821be0aSBarry Smith .seealso: [](sec_pcmpi), `PCMPIServerEnd()`, `PCMPI`, `KSPCheckPCMPI()`
430f1f2ae84SBarry Smith @*/
431d71ae5a4SJacob Faibussowitsch PetscErrorCode PCMPIServerBegin(void)
432d71ae5a4SJacob Faibussowitsch {
433f1f2ae84SBarry Smith   PetscMPIInt rank;
434f1f2ae84SBarry Smith 
435f1f2ae84SBarry Smith   PetscFunctionBegin;
4369d3446b2SPierre Jolivet   PetscCall(PetscInfo(NULL, "Starting MPI Linear Solver Server\n"));
4375e1a0e3cSBarry Smith   if (PetscDefined(USE_SINGLE_LIBRARY)) {
4385e1a0e3cSBarry Smith     PetscCall(VecInitializePackage());
4395e1a0e3cSBarry Smith     PetscCall(MatInitializePackage());
4405e1a0e3cSBarry Smith     PetscCall(DMInitializePackage());
4415e1a0e3cSBarry Smith     PetscCall(PCInitializePackage());
4425e1a0e3cSBarry Smith     PetscCall(KSPInitializePackage());
4435e1a0e3cSBarry Smith     PetscCall(SNESInitializePackage());
4445e1a0e3cSBarry Smith     PetscCall(TSInitializePackage());
4455e1a0e3cSBarry Smith     PetscCall(TaoInitializePackage());
4465e1a0e3cSBarry Smith   }
447956255efSBarry Smith   PetscCall(PetscLogStageRegister("PCMPI", &PCMPIStage));
4485e1a0e3cSBarry Smith 
449f1f2ae84SBarry Smith   PetscCallMPI(MPI_Comm_rank(PC_MPI_COMM_WORLD, &rank));
450f1f2ae84SBarry Smith   if (rank == 0) {
451f1f2ae84SBarry Smith     PETSC_COMM_WORLD  = PETSC_COMM_SELF;
4523821be0aSBarry Smith     PCMPIServerActive = PETSC_TRUE;
4533ba16761SJacob Faibussowitsch     PetscFunctionReturn(PETSC_SUCCESS);
454f1f2ae84SBarry Smith   }
455f1f2ae84SBarry Smith 
456f1f2ae84SBarry Smith   while (PETSC_TRUE) {
457f1f2ae84SBarry Smith     PCMPICommand request = PCMPI_CREATE;
458f1f2ae84SBarry Smith     PetscCallMPI(MPI_Bcast(&request, 1, MPIU_ENUM, 0, PC_MPI_COMM_WORLD));
459f1f2ae84SBarry Smith     switch (request) {
460d71ae5a4SJacob Faibussowitsch     case PCMPI_CREATE:
461d71ae5a4SJacob Faibussowitsch       PetscCall(PCMPICreate(NULL));
462d71ae5a4SJacob Faibussowitsch       break;
463d71ae5a4SJacob Faibussowitsch     case PCMPI_SET_MAT:
464d71ae5a4SJacob Faibussowitsch       PetscCall(PCMPISetMat(NULL));
465d71ae5a4SJacob Faibussowitsch       break;
466d71ae5a4SJacob Faibussowitsch     case PCMPI_UPDATE_MAT_VALUES:
467d71ae5a4SJacob Faibussowitsch       PetscCall(PCMPIUpdateMatValues(NULL));
468d71ae5a4SJacob Faibussowitsch       break;
469f1f2ae84SBarry Smith     case PCMPI_VIEW:
470f1f2ae84SBarry Smith       // PetscCall(PCMPIView(NULL));
471f1f2ae84SBarry Smith       break;
472d71ae5a4SJacob Faibussowitsch     case PCMPI_SOLVE:
473d71ae5a4SJacob Faibussowitsch       PetscCall(PCMPISolve(NULL, NULL, NULL));
474d71ae5a4SJacob Faibussowitsch       break;
475d71ae5a4SJacob Faibussowitsch     case PCMPI_DESTROY:
476d71ae5a4SJacob Faibussowitsch       PetscCall(PCMPIDestroy(NULL));
477d71ae5a4SJacob Faibussowitsch       break;
478f1f2ae84SBarry Smith     case PCMPI_EXIT:
479f1f2ae84SBarry Smith       PetscCall(PetscFinalize());
480f1f2ae84SBarry Smith       exit(0); /* not sure if this is a good idea, but cannot return because it will run users main program */
481f1f2ae84SBarry Smith       break;
482d71ae5a4SJacob Faibussowitsch     default:
483d71ae5a4SJacob Faibussowitsch       break;
484f1f2ae84SBarry Smith     }
485f1f2ae84SBarry Smith   }
4863ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
487f1f2ae84SBarry Smith }
488f1f2ae84SBarry Smith 
489f1f2ae84SBarry Smith /*@C
490f1f2ae84SBarry Smith   PCMPIServerEnd - ends a server that runs on the rank != 0 MPI processes waiting to process requests for
491f1580f4eSBarry Smith   parallel KSP solves and management of parallel `KSP` objects.
492f1f2ae84SBarry Smith 
49320f4b53cSBarry Smith   Logically Collective on all MPI ranks except 0
49420f4b53cSBarry Smith 
49520f4b53cSBarry Smith   Level: developer
496f1f2ae84SBarry Smith 
497f1580f4eSBarry Smith   Note:
498f1f2ae84SBarry Smith   This is normally ended automatically in `PetscFinalize()` when the option is provided
499f1f2ae84SBarry Smith 
5003821be0aSBarry Smith .seealso: [](sec_pcmpi), `PCMPIServerBegin()`, `PCMPI`, `KSPCheckPCMPI()`
501f1f2ae84SBarry Smith @*/
502d71ae5a4SJacob Faibussowitsch PetscErrorCode PCMPIServerEnd(void)
503d71ae5a4SJacob Faibussowitsch {
504f1f2ae84SBarry Smith   PCMPICommand request = PCMPI_EXIT;
505f1f2ae84SBarry Smith 
506f1f2ae84SBarry Smith   PetscFunctionBegin;
507f1f2ae84SBarry Smith   if (PetscGlobalRank == 0) {
508f1f2ae84SBarry Smith     PetscViewer       viewer = NULL;
509f1f2ae84SBarry Smith     PetscViewerFormat format;
510f1f2ae84SBarry Smith 
511f1f2ae84SBarry Smith     PetscCallMPI(MPI_Bcast(&request, 1, MPIU_ENUM, 0, PC_MPI_COMM_WORLD));
512f1f2ae84SBarry Smith     PETSC_COMM_WORLD = MPI_COMM_WORLD; /* could use PC_MPI_COMM_WORLD */
513f1f2ae84SBarry Smith     PetscOptionsBegin(PETSC_COMM_SELF, NULL, "MPI linear solver server options", NULL);
514f1f2ae84SBarry Smith     PetscCall(PetscOptionsViewer("-mpi_linear_solver_server_view", "View information about system solved with the server", "PCMPI", &viewer, &format, NULL));
515f1f2ae84SBarry Smith     PetscOptionsEnd();
516f1f2ae84SBarry Smith     if (viewer) {
517f1f2ae84SBarry Smith       PetscBool isascii;
518f1f2ae84SBarry Smith 
519f1f2ae84SBarry Smith       PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
520f1f2ae84SBarry Smith       if (isascii) {
521f1f2ae84SBarry Smith         PetscMPIInt size;
5225316cbedSBarry Smith         PetscMPIInt i;
523f1f2ae84SBarry Smith 
524f1f2ae84SBarry Smith         PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
5255316cbedSBarry Smith         PetscCall(PetscViewerASCIIPrintf(viewer, "MPI linear solver server statistics:\n"));
5265316cbedSBarry Smith         PetscCall(PetscViewerASCIIPrintf(viewer, "    Ranks        KSPSolve()s     Mats        KSPs       Avg. Size      Avg. Its\n"));
5275316cbedSBarry Smith         if (PCMPIKSPCountsSeq) {
5285316cbedSBarry Smith           PetscCall(PetscViewerASCIIPrintf(viewer, "  Sequential         %" PetscInt_FMT "                         %" PetscInt_FMT "            %" PetscInt_FMT "           %" PetscInt_FMT "\n", PCMPISolveCountsSeq, PCMPIKSPCountsSeq, PCMPISizesSeq / PCMPISolveCountsSeq, PCMPIIterationsSeq / PCMPISolveCountsSeq));
529f1f2ae84SBarry Smith         }
5305316cbedSBarry Smith         for (i = 0; i < size; i++) {
5315316cbedSBarry Smith           if (PCMPIKSPCounts[i]) {
5325316cbedSBarry Smith             PetscCall(PetscViewerASCIIPrintf(viewer, "     %d               %" PetscInt_FMT "            %" PetscInt_FMT "           %" PetscInt_FMT "            %" PetscInt_FMT "            %" PetscInt_FMT "\n", i + 1, PCMPISolveCounts[i], PCMPIMatCounts[i], PCMPIKSPCounts[i], PCMPISizes[i] / PCMPISolveCounts[i], PCMPIIterations[i] / PCMPISolveCounts[i]));
5335316cbedSBarry Smith           }
5345316cbedSBarry Smith         }
535f1f2ae84SBarry Smith       }
536cd791dc2SBarry Smith       PetscCall(PetscOptionsRestoreViewer(&viewer));
537f1f2ae84SBarry Smith     }
538f1f2ae84SBarry Smith   }
539f1f2ae84SBarry Smith   PetscCall(PCMPICommsDestroy());
5403821be0aSBarry Smith   PCMPIServerActive = PETSC_FALSE;
5413ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
542f1f2ae84SBarry Smith }
543f1f2ae84SBarry Smith 
544f1f2ae84SBarry Smith /*
545f1f2ae84SBarry Smith     This version is used in the trivial case when the MPI parallel solver server is running on just the original MPI rank 0
546f1f2ae84SBarry Smith     because, for example, the problem is small. This version is more efficient because it does not require copying any data
547f1f2ae84SBarry Smith */
548d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCSetUp_Seq(PC pc)
549d71ae5a4SJacob Faibussowitsch {
550f1f2ae84SBarry Smith   PC_MPI     *km = (PC_MPI *)pc->data;
551f1f2ae84SBarry Smith   Mat         sA;
552f1f2ae84SBarry Smith   const char *prefix;
553f9818f3cSJose E. Roman   char       *found = NULL, *cprefix;
554f1f2ae84SBarry Smith 
555f1f2ae84SBarry Smith   PetscFunctionBegin;
556f1f2ae84SBarry Smith   PetscCall(PCGetOperators(pc, NULL, &sA));
557f1f2ae84SBarry Smith   PetscCall(PCGetOptionsPrefix(pc, &prefix));
558f1f2ae84SBarry Smith   PetscCall(KSPCreate(PETSC_COMM_SELF, &km->ksps[0]));
5593821be0aSBarry Smith   PetscCall(KSPSetNestLevel(km->ksps[0], 1));
5603821be0aSBarry Smith   PetscCall(PetscObjectSetTabLevel((PetscObject)km->ksps[0], 1));
5613821be0aSBarry Smith 
5623821be0aSBarry Smith   /* Created KSP gets prefix of PC minus the mpi_linear_solver_server_ portion */
5633821be0aSBarry Smith   PetscCall(PCGetOptionsPrefix(pc, &prefix));
5643821be0aSBarry Smith   PetscCheck(prefix, PETSC_COMM_SELF, PETSC_ERR_PLIB, "PCMPI missing required prefix");
5653821be0aSBarry Smith   PetscCall(PetscStrallocpy(prefix, &cprefix));
5663821be0aSBarry Smith   PetscCall(PetscStrstr(cprefix, "mpi_linear_solver_server_", &found));
5673821be0aSBarry Smith   PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_PLIB, "PCMPI missing mpi_linear_solver_server_ portion of prefix");
5683821be0aSBarry Smith   *found = 0;
5693821be0aSBarry Smith   PetscCall(KSPSetOptionsPrefix(km->ksps[0], cprefix));
5703821be0aSBarry Smith   PetscCall(PetscFree(cprefix));
5713821be0aSBarry Smith 
572f1f2ae84SBarry Smith   PetscCall(KSPSetOperators(km->ksps[0], sA, sA));
573f1f2ae84SBarry Smith   PetscCall(KSPSetFromOptions(km->ksps[0]));
574f1f2ae84SBarry Smith   PetscCall(KSPSetUp(km->ksps[0]));
5753ba16761SJacob Faibussowitsch   PetscCall(PetscInfo((PetscObject)pc, "MPI parallel linear solver system is being solved directly on rank 0 due to its small size\n"));
576f1f2ae84SBarry Smith   PCMPIKSPCountsSeq++;
5773ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
578f1f2ae84SBarry Smith }
579f1f2ae84SBarry Smith 
580d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCApply_Seq(PC pc, Vec b, Vec x)
581d71ae5a4SJacob Faibussowitsch {
582f1f2ae84SBarry Smith   PC_MPI  *km = (PC_MPI *)pc->data;
5835316cbedSBarry Smith   PetscInt its, n;
5845316cbedSBarry Smith   Mat      A;
585f1f2ae84SBarry Smith 
586f1f2ae84SBarry Smith   PetscFunctionBegin;
587f1f2ae84SBarry Smith   PetscCall(KSPSolve(km->ksps[0], b, x));
5885316cbedSBarry Smith   PetscCall(KSPGetIterationNumber(km->ksps[0], &its));
589f1f2ae84SBarry Smith   PCMPISolveCountsSeq++;
5905316cbedSBarry Smith   PCMPIIterationsSeq += its;
5915316cbedSBarry Smith   PetscCall(KSPGetOperators(km->ksps[0], NULL, &A));
5925316cbedSBarry Smith   PetscCall(MatGetSize(A, &n, NULL));
5935316cbedSBarry Smith   PCMPISizesSeq += n;
5943ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
595f1f2ae84SBarry Smith }
596f1f2ae84SBarry Smith 
597d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCView_Seq(PC pc, PetscViewer viewer)
598d71ae5a4SJacob Faibussowitsch {
599f1f2ae84SBarry Smith   PC_MPI *km = (PC_MPI *)pc->data;
600f1f2ae84SBarry Smith 
601f1f2ae84SBarry Smith   PetscFunctionBegin;
602f1f2ae84SBarry Smith   PetscCall(PetscViewerASCIIPrintf(viewer, "Running MPI linear solver server directly on rank 0 due to its small size\n"));
603f1f2ae84SBarry Smith   PetscCall(PetscViewerASCIIPrintf(viewer, "Desired minimum number of nonzeros per rank for MPI parallel solve %d\n", (int)km->mincntperrank));
6045316cbedSBarry Smith   PetscCall(PetscViewerASCIIPrintf(viewer, "*** Use -mpi_linear_solver_server_view to statistics on all the solves ***\n"));
6053ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
606f1f2ae84SBarry Smith }
607f1f2ae84SBarry Smith 
608d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCDestroy_Seq(PC pc)
609d71ae5a4SJacob Faibussowitsch {
610f1f2ae84SBarry Smith   PC_MPI *km = (PC_MPI *)pc->data;
611f1f2ae84SBarry Smith 
612f1f2ae84SBarry Smith   PetscFunctionBegin;
613f1f2ae84SBarry Smith   PetscCall(KSPDestroy(&km->ksps[0]));
614f1f2ae84SBarry Smith   PetscCall(PetscFree(pc->data));
6153ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
616f1f2ae84SBarry Smith }
617f1f2ae84SBarry Smith 
618f1f2ae84SBarry Smith /*
619f1f2ae84SBarry Smith      PCSetUp_MPI - Trigger the creation of the MPI parallel PC and copy parts of the matrix and
620*dd8e379bSPierre Jolivet      right-hand side to the parallel PC
621f1f2ae84SBarry Smith */
622d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCSetUp_MPI(PC pc)
623d71ae5a4SJacob Faibussowitsch {
624f1f2ae84SBarry Smith   PC_MPI      *km = (PC_MPI *)pc->data;
625f1f2ae84SBarry Smith   PetscMPIInt  rank, size;
626f1f2ae84SBarry Smith   PCMPICommand request;
627f1f2ae84SBarry Smith   PetscBool    newmatrix = PETSC_FALSE;
628f1f2ae84SBarry Smith 
629f1f2ae84SBarry Smith   PetscFunctionBegin;
630f1f2ae84SBarry Smith   PetscCallMPI(MPI_Comm_rank(MPI_COMM_WORLD, &rank));
631f1f2ae84SBarry Smith   PetscCheck(rank == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "PCMPI can only be used from 0th rank of MPI_COMM_WORLD. Perhaps a missing -mpi_linear_solver_server?");
632f1f2ae84SBarry Smith   PetscCallMPI(MPI_Comm_size(MPI_COMM_WORLD, &size));
633f1f2ae84SBarry Smith 
634f1f2ae84SBarry Smith   if (!pc->setupcalled) {
635f1f2ae84SBarry Smith     if (!km->alwaysuseserver) {
636f1f2ae84SBarry Smith       PetscInt n;
637f1f2ae84SBarry Smith       Mat      sA;
638f1f2ae84SBarry Smith       /* short circuit for small systems */
639f1f2ae84SBarry Smith       PetscCall(PCGetOperators(pc, &sA, &sA));
640f1f2ae84SBarry Smith       PetscCall(MatGetSize(sA, &n, NULL));
641f1f2ae84SBarry Smith       if (n < 2 * km->mincntperrank - 1 || size == 1) {
642f1f2ae84SBarry Smith         pc->ops->setup   = NULL;
643f1f2ae84SBarry Smith         pc->ops->apply   = PCApply_Seq;
644f1f2ae84SBarry Smith         pc->ops->destroy = PCDestroy_Seq;
645f1f2ae84SBarry Smith         pc->ops->view    = PCView_Seq;
646f1f2ae84SBarry Smith         PetscCall(PCSetUp_Seq(pc));
6473ba16761SJacob Faibussowitsch         PetscFunctionReturn(PETSC_SUCCESS);
648f1f2ae84SBarry Smith       }
649f1f2ae84SBarry Smith     }
650f1f2ae84SBarry Smith 
651f1f2ae84SBarry Smith     request = PCMPI_CREATE;
652f1f2ae84SBarry Smith     PetscCallMPI(MPI_Bcast(&request, 1, MPIU_ENUM, 0, MPI_COMM_WORLD));
653f1f2ae84SBarry Smith     PetscCall(PCMPICreate(pc));
654f1f2ae84SBarry Smith     newmatrix = PETSC_TRUE;
6559371c9d4SSatish Balay   }
6569371c9d4SSatish Balay   if (pc->flag == DIFFERENT_NONZERO_PATTERN) newmatrix = PETSC_TRUE;
657f1f2ae84SBarry Smith 
658f1f2ae84SBarry Smith   if (newmatrix) {
6593ba16761SJacob Faibussowitsch     PetscCall(PetscInfo((PetscObject)pc, "New matrix or matrix has changed nonzero structure\n"));
660f1f2ae84SBarry Smith     request = PCMPI_SET_MAT;
661f1f2ae84SBarry Smith     PetscCallMPI(MPI_Bcast(&request, 1, MPIU_ENUM, 0, MPI_COMM_WORLD));
662f1f2ae84SBarry Smith     PetscCall(PCMPISetMat(pc));
663f1f2ae84SBarry Smith   } else {
664bbea24aaSStefano Zampini     PetscCall(PetscInfo((PetscObject)pc, "Matrix has only changed nonzero values\n"));
665f1f2ae84SBarry Smith     request = PCMPI_UPDATE_MAT_VALUES;
666f1f2ae84SBarry Smith     PetscCallMPI(MPI_Bcast(&request, 1, MPIU_ENUM, 0, MPI_COMM_WORLD));
667f1f2ae84SBarry Smith     PetscCall(PCMPIUpdateMatValues(pc));
668f1f2ae84SBarry Smith   }
6693ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
670f1f2ae84SBarry Smith }
671f1f2ae84SBarry Smith 
672d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCApply_MPI(PC pc, Vec b, Vec x)
673d71ae5a4SJacob Faibussowitsch {
674f1f2ae84SBarry Smith   PCMPICommand request = PCMPI_SOLVE;
675f1f2ae84SBarry Smith 
676f1f2ae84SBarry Smith   PetscFunctionBegin;
677f1f2ae84SBarry Smith   PetscCallMPI(MPI_Bcast(&request, 1, MPIU_ENUM, 0, MPI_COMM_WORLD));
678f1f2ae84SBarry Smith   PetscCall(PCMPISolve(pc, b, x));
6793ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
680f1f2ae84SBarry Smith }
681f1f2ae84SBarry Smith 
68266976f2fSJacob Faibussowitsch static PetscErrorCode PCDestroy_MPI(PC pc)
683d71ae5a4SJacob Faibussowitsch {
684f1f2ae84SBarry Smith   PCMPICommand request = PCMPI_DESTROY;
685f1f2ae84SBarry Smith 
686f1f2ae84SBarry Smith   PetscFunctionBegin;
687f1f2ae84SBarry Smith   PetscCallMPI(MPI_Bcast(&request, 1, MPIU_ENUM, 0, MPI_COMM_WORLD));
688f1f2ae84SBarry Smith   PetscCall(PCMPIDestroy(pc));
689f1f2ae84SBarry Smith   PetscCall(PetscFree(pc->data));
6903ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
691f1f2ae84SBarry Smith }
692f1f2ae84SBarry Smith 
693f1f2ae84SBarry Smith /*
694f1f2ae84SBarry Smith      PCView_MPI - Cannot call view on the MPI parallel KSP because other ranks do not have access to the viewer
695f1f2ae84SBarry Smith */
69666976f2fSJacob Faibussowitsch static PetscErrorCode PCView_MPI(PC pc, PetscViewer viewer)
697d71ae5a4SJacob Faibussowitsch {
698f1f2ae84SBarry Smith   PC_MPI     *km = (PC_MPI *)pc->data;
699f1f2ae84SBarry Smith   MPI_Comm    comm;
700f1f2ae84SBarry Smith   PetscMPIInt size;
701f1f2ae84SBarry Smith 
702f1f2ae84SBarry Smith   PetscFunctionBegin;
703f1f2ae84SBarry Smith   PetscCall(PetscObjectGetComm((PetscObject)km->ksps[0], &comm));
704f1f2ae84SBarry Smith   PetscCallMPI(MPI_Comm_size(comm, &size));
705f1f2ae84SBarry Smith   PetscCall(PetscViewerASCIIPrintf(viewer, "Size of MPI communicator used for MPI parallel KSP solve %d\n", size));
706f1f2ae84SBarry Smith   PetscCall(PetscViewerASCIIPrintf(viewer, "Desired minimum number of nonzeros per rank for MPI parallel solve %d\n", (int)km->mincntperrank));
70768a21331SBarry Smith   PetscCall(PetscViewerASCIIPrintf(viewer, "*** Use -mpi_linear_solver_server_view to statistics on all the solves ***\n"));
7083ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
709f1f2ae84SBarry Smith }
710f1f2ae84SBarry Smith 
71166976f2fSJacob Faibussowitsch static PetscErrorCode PCSetFromOptions_MPI(PC pc, PetscOptionItems *PetscOptionsObject)
712d71ae5a4SJacob Faibussowitsch {
713f1f2ae84SBarry Smith   PC_MPI *km = (PC_MPI *)pc->data;
714f1f2ae84SBarry Smith 
715f1f2ae84SBarry Smith   PetscFunctionBegin;
716f1f2ae84SBarry Smith   PetscOptionsHeadBegin(PetscOptionsObject, "MPI linear solver server options");
7173821be0aSBarry Smith   PetscCall(PetscOptionsInt("-minimum_count_per_rank", "Desired minimum number of nonzeros per rank", "None", km->mincntperrank, &km->mincntperrank, NULL));
7183821be0aSBarry Smith   PetscCall(PetscOptionsBool("-always_use_server", "Use the server even if only one rank is used for the solve (for debugging)", "None", km->alwaysuseserver, &km->alwaysuseserver, NULL));
719f1f2ae84SBarry Smith   PetscOptionsHeadEnd();
7203ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
721f1f2ae84SBarry Smith }
722f1f2ae84SBarry Smith 
723f1f2ae84SBarry Smith /*MC
724f1580f4eSBarry Smith      PCMPI - Calls an MPI parallel `KSP` to solve a linear system from user code running on one process
725f1f2ae84SBarry Smith 
7263821be0aSBarry Smith    Options Database Keys for the Server:
727f1f2ae84SBarry Smith +  -mpi_linear_solver_server - causes the PETSc program to start in MPI linear solver server mode where only the first MPI rank runs user code
7283821be0aSBarry Smith -  -mpi_linear_solver_server_view - displays information about all the linear systems solved by the MPI linear solver server
729f1f2ae84SBarry Smith 
7303821be0aSBarry Smith    Options Database Keys for a specific `KSP` object
7313821be0aSBarry Smith +  -[any_ksp_prefix]_mpi_linear_solver_server_minimum_count_per_rank - sets the minimum size of the linear system per MPI rank that the solver will strive for
7323821be0aSBarry Smith -  -[any_ksp_prefix]_mpi_linear_solver_server_always_use_server - use the server solver code even if the particular system is only solved on the process (for debugging and testing purposes)
7333821be0aSBarry Smith 
7343821be0aSBarry Smith    Level: developer
73520f4b53cSBarry Smith 
736f1f2ae84SBarry Smith    Notes:
73746bbbc36SPierre Jolivet    The options database prefix for the actual solver is any prefix provided before use to the original `KSP` with `KSPSetOptionsPrefix()`, mostly commonly no prefix is used.
738f1f2ae84SBarry Smith 
739f1f2ae84SBarry Smith    It can be particularly useful for user OpenMP code or potentially user GPU code.
740f1f2ae84SBarry Smith 
741*dd8e379bSPierre Jolivet    When the program is running with a single MPI process then it directly uses the provided matrix and right-hand side
7423821be0aSBarry Smith    and does not need to distribute the matrix and vector to the various MPI processes; thus it incurs no extra overhead over just using the `KSP` directly.
743f1f2ae84SBarry Smith 
7443821be0aSBarry Smith    The solver options for actual solving `KSP` and `PC` must be controlled via the options database, calls to set options directly on the user level `KSP` and `PC` have no effect
7450316ec64SBarry Smith    because they are not the actual solver objects.
7460316ec64SBarry Smith 
7470316ec64SBarry Smith    When `-log_view` is used with this solver the events within the parallel solve are logging in their own stage. Some of the logging in the other
74868a21331SBarry Smith    stages will be confusing since the event times are only recorded on the 0th MPI rank, thus the percent of time in the events will be misleading.
7490316ec64SBarry Smith 
7503821be0aSBarry Smith    Developer Note:
7513821be0aSBarry Smith    This `PCType` is never directly selected by the user, it is set when the option `-mpi_linear_solver_server` is used and the `PC` is at the outer most nesting of
7523821be0aSBarry Smith    a `KSP`. The outer most `KSP` object is automatically set to `KSPPREONLY` and thus is not directly visible to the user.
7533821be0aSBarry Smith 
7543821be0aSBarry Smith .seealso: [](sec_pcmpi), `KSPCreate()`, `KSPSetType()`, `KSPType`, `KSP`, `PC`, `PCMPIServerBegin()`, `PCMPIServerEnd()`, `KSPCheckPCMPI()`
755f1f2ae84SBarry Smith M*/
756d71ae5a4SJacob Faibussowitsch PETSC_EXTERN PetscErrorCode PCCreate_MPI(PC pc)
757d71ae5a4SJacob Faibussowitsch {
758f1f2ae84SBarry Smith   PC_MPI *km;
759f9818f3cSJose E. Roman   char   *found = NULL;
760f1f2ae84SBarry Smith 
761f1f2ae84SBarry Smith   PetscFunctionBegin;
7623821be0aSBarry Smith   PetscCall(PetscStrstr(((PetscObject)pc)->prefix, "mpi_linear_solver_server_", &found));
7633821be0aSBarry Smith   PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_PLIB, "PCMPI object prefix does not have mpi_linear_solver_server_");
7643821be0aSBarry Smith 
7653821be0aSBarry Smith   /* material from PCSetType() */
7663821be0aSBarry Smith   PetscTryTypeMethod(pc, destroy);
7673821be0aSBarry Smith   pc->ops->destroy = NULL;
7683821be0aSBarry Smith   pc->data         = NULL;
7693821be0aSBarry Smith 
7703821be0aSBarry Smith   PetscCall(PetscFunctionListDestroy(&((PetscObject)pc)->qlist));
7713821be0aSBarry Smith   PetscCall(PetscMemzero(pc->ops, sizeof(struct _PCOps)));
7723821be0aSBarry Smith   pc->modifysubmatrices  = NULL;
7733821be0aSBarry Smith   pc->modifysubmatricesP = NULL;
7743821be0aSBarry Smith   pc->setupcalled        = 0;
7753821be0aSBarry Smith 
7764dfa11a4SJacob Faibussowitsch   PetscCall(PetscNew(&km));
777f1f2ae84SBarry Smith   pc->data = (void *)km;
778f1f2ae84SBarry Smith 
779f1f2ae84SBarry Smith   km->mincntperrank = 10000;
780f1f2ae84SBarry Smith 
781f1f2ae84SBarry Smith   pc->ops->setup          = PCSetUp_MPI;
782f1f2ae84SBarry Smith   pc->ops->apply          = PCApply_MPI;
783f1f2ae84SBarry Smith   pc->ops->destroy        = PCDestroy_MPI;
784f1f2ae84SBarry Smith   pc->ops->view           = PCView_MPI;
785f1f2ae84SBarry Smith   pc->ops->setfromoptions = PCSetFromOptions_MPI;
7863821be0aSBarry Smith   PetscCall(PetscObjectChangeTypeName((PetscObject)pc, PCMPI));
7873ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
788f1f2ae84SBarry Smith }
789