xref: /petsc/src/ksp/pc/impls/redistribute/redistribute.c (revision 3ae97395161b37b5a2ff0c3902240cd0ff742878)
1df826632SBarry Smith 
2df826632SBarry Smith /*
3df826632SBarry Smith   This file defines a "solve the problem redistributely on each subgroup of processor" preconditioner.
4df826632SBarry Smith */
5af0996ceSBarry Smith #include <petsc/private/pcimpl.h> /*I "petscksp.h" I*/
6c6db04a5SJed Brown #include <petscksp.h>
7df826632SBarry Smith 
869ff637eSBarry Smith typedef struct _PC_FieldSplitLink *PC_FieldSplitLink;
969ff637eSBarry Smith struct _PC_FieldSplitLink {
1069ff637eSBarry Smith   char             *splitname;
1169ff637eSBarry Smith   IS                is;
1269ff637eSBarry Smith   PC_FieldSplitLink next, previous;
1369ff637eSBarry Smith };
1469ff637eSBarry Smith 
15df826632SBarry Smith typedef struct {
16df826632SBarry Smith   KSP          ksp;
17df826632SBarry Smith   Vec          x, b;
18df826632SBarry Smith   VecScatter   scatter;
19911f9fe8SBarry Smith   IS           is;
20181dd334SBarry Smith   PetscInt     dcnt, *drows; /* these are the local rows that have only diagonal entry */
21181dd334SBarry Smith   PetscScalar *diag;
22181dd334SBarry Smith   Vec          work;
2387b47708SBarry Smith   PetscBool    zerodiag;
2469ff637eSBarry Smith 
2569ff637eSBarry Smith   PetscInt          nsplits;
2669ff637eSBarry Smith   PC_FieldSplitLink splitlinks;
27df826632SBarry Smith } PC_Redistribute;
28df826632SBarry Smith 
2969ff637eSBarry Smith static PetscErrorCode PCFieldSplitSetIS_Redistribute(PC pc, const char splitname[], IS is)
3069ff637eSBarry Smith {
3169ff637eSBarry Smith   PC_Redistribute   *red  = (PC_Redistribute *)pc->data;
3269ff637eSBarry Smith   PC_FieldSplitLink *next = &red->splitlinks;
3369ff637eSBarry Smith 
3469ff637eSBarry Smith   PetscFunctionBegin;
3569ff637eSBarry Smith   while (*next) next = &(*next)->next;
3669ff637eSBarry Smith   PetscCall(PetscNew(next));
3769ff637eSBarry Smith   if (splitname) {
3869ff637eSBarry Smith     PetscCall(PetscStrallocpy(splitname, &(*next)->splitname));
3969ff637eSBarry Smith   } else {
4069ff637eSBarry Smith     PetscCall(PetscMalloc1(8, &(*next)->splitname));
4169ff637eSBarry Smith     PetscCall(PetscSNPrintf((*next)->splitname, 7, "%" PetscInt_FMT, red->nsplits++));
4269ff637eSBarry Smith   }
4369ff637eSBarry Smith   PetscCall(PetscObjectReference((PetscObject)is));
4469ff637eSBarry Smith   PetscCall(ISDestroy(&(*next)->is));
4569ff637eSBarry Smith   (*next)->is = is;
4669ff637eSBarry Smith   PetscFunctionReturn(PETSC_SUCCESS);
4769ff637eSBarry Smith }
4869ff637eSBarry Smith 
49d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCView_Redistribute(PC pc, PetscViewer viewer)
50d71ae5a4SJacob Faibussowitsch {
51df826632SBarry Smith   PC_Redistribute *red = (PC_Redistribute *)pc->data;
52ace3abfcSBarry Smith   PetscBool        iascii, isstring;
53181dd334SBarry Smith   PetscInt         ncnt, N;
54df826632SBarry Smith 
55df826632SBarry Smith   PetscFunctionBegin;
569566063dSJacob Faibussowitsch   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
579566063dSJacob Faibussowitsch   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERSTRING, &isstring));
58df826632SBarry Smith   if (iascii) {
591c2dc1cbSBarry Smith     PetscCall(MPIU_Allreduce(&red->dcnt, &ncnt, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)pc)));
609566063dSJacob Faibussowitsch     PetscCall(MatGetSize(pc->pmat, &N, NULL));
6163a3b9bcSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "    Number rows eliminated %" PetscInt_FMT " Percentage rows eliminated %g\n", ncnt, (double)(100.0 * ((PetscReal)ncnt) / ((PetscReal)N))));
629566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "  Redistribute preconditioner: \n"));
639566063dSJacob Faibussowitsch     PetscCall(KSPView(red->ksp, viewer));
64df826632SBarry Smith   } else if (isstring) {
659566063dSJacob Faibussowitsch     PetscCall(PetscViewerStringSPrintf(viewer, " Redistribute preconditioner"));
669566063dSJacob Faibussowitsch     PetscCall(KSPView(red->ksp, viewer));
6711aeaf0aSBarry Smith   }
683ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
69df826632SBarry Smith }
70df826632SBarry Smith 
71d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCSetUp_Redistribute(PC pc)
72d71ae5a4SJacob Faibussowitsch {
73df826632SBarry Smith   PC_Redistribute         *red = (PC_Redistribute *)pc->data;
74df826632SBarry Smith   MPI_Comm                 comm;
7569ff637eSBarry Smith   PetscInt                 rstart, rend, nrstart, nrend, i, nz, cnt, *rows, ncnt, dcnt, *drows;
7626283091SBarry Smith   PetscLayout              map, nmap;
77ec4bef21SJose E. Roman   PetscMPIInt              size, tag, n;
78ec4bef21SJose E. Roman   PETSC_UNUSED PetscMPIInt imdex;
790298fd71SBarry Smith   PetscInt                *source = NULL;
8076ec1555SBarry Smith   PetscMPIInt             *sizes  = NULL, nrecvs;
81df826632SBarry Smith   PetscInt                 j, nsends;
820298fd71SBarry Smith   PetscInt                *owner = NULL, *starts = NULL, count, slen;
83e8dd6687SHong Zhang   PetscInt                *rvalues, *svalues, recvtotal;
84df826632SBarry Smith   PetscMPIInt             *onodes1, *olengths1;
850298fd71SBarry Smith   MPI_Request             *send_waits = NULL, *recv_waits = NULL;
86df826632SBarry Smith   MPI_Status               recv_status, *send_status;
87181dd334SBarry Smith   Vec                      tvec, diag;
88ca320bd4SBarry Smith   Mat                      tmat;
89003249c0SBarry Smith   const PetscScalar       *d, *values;
90003249c0SBarry Smith   const PetscInt          *cols;
9169ff637eSBarry Smith   PC_FieldSplitLink       *next = &red->splitlinks;
92df826632SBarry Smith 
93df826632SBarry Smith   PetscFunctionBegin;
94dc9360f3SBarry Smith   if (pc->setupcalled) {
9569ff637eSBarry Smith     PetscCheck(pc->flag == SAME_NONZERO_PATTERN, PetscObjectComm((PetscObject)pc), PETSC_ERR_SUP, "PC is not supported for a change in the nonzero structure of the matrix");
969566063dSJacob Faibussowitsch     PetscCall(KSPGetOperators(red->ksp, NULL, &tmat));
979566063dSJacob Faibussowitsch     PetscCall(MatCreateSubMatrix(pc->pmat, red->is, red->is, MAT_REUSE_MATRIX, &tmat));
989566063dSJacob Faibussowitsch     PetscCall(KSPSetOperators(red->ksp, tmat, tmat));
99dc9360f3SBarry Smith   } else {
100b862ddfaSBarry Smith     PetscInt          NN;
10169ff637eSBarry Smith     PC                ipc;
10269ff637eSBarry Smith     PetscVoidFunction fptr;
103b862ddfaSBarry Smith 
1049566063dSJacob Faibussowitsch     PetscCall(PetscObjectGetComm((PetscObject)pc, &comm));
1059566063dSJacob Faibussowitsch     PetscCallMPI(MPI_Comm_size(comm, &size));
1069566063dSJacob Faibussowitsch     PetscCall(PetscObjectGetNewTag((PetscObject)pc, &tag));
107df826632SBarry Smith 
108ca320bd4SBarry Smith     /* count non-diagonal rows on process */
1099566063dSJacob Faibussowitsch     PetscCall(MatGetOwnershipRange(pc->mat, &rstart, &rend));
110ca320bd4SBarry Smith     cnt = 0;
111df826632SBarry Smith     for (i = rstart; i < rend; i++) {
1129566063dSJacob Faibussowitsch       PetscCall(MatGetRow(pc->mat, i, &nz, &cols, &values));
113003249c0SBarry Smith       for (PetscInt j = 0; j < nz; j++) {
114003249c0SBarry Smith         if (values[j] != 0 && cols[j] != i) {
115003249c0SBarry Smith           cnt++;
116003249c0SBarry Smith           break;
117003249c0SBarry Smith         }
118003249c0SBarry Smith       }
1199566063dSJacob Faibussowitsch       PetscCall(MatRestoreRow(pc->mat, i, &nz, &cols, &values));
120df826632SBarry Smith     }
1219566063dSJacob Faibussowitsch     PetscCall(PetscMalloc1(cnt, &rows));
1229566063dSJacob Faibussowitsch     PetscCall(PetscMalloc1(rend - rstart - cnt, &drows));
123ca320bd4SBarry Smith 
124ca320bd4SBarry Smith     /* list non-diagonal rows on process */
1259371c9d4SSatish Balay     cnt  = 0;
1269371c9d4SSatish Balay     dcnt = 0;
127df826632SBarry Smith     for (i = rstart; i < rend; i++) {
128003249c0SBarry Smith       PetscBool diagonly = PETSC_TRUE;
1299566063dSJacob Faibussowitsch       PetscCall(MatGetRow(pc->mat, i, &nz, &cols, &values));
130003249c0SBarry Smith       for (PetscInt j = 0; j < nz; j++) {
131003249c0SBarry Smith         if (values[j] != 0 && cols[j] != i) {
132003249c0SBarry Smith           diagonly = PETSC_FALSE;
133003249c0SBarry Smith           break;
134003249c0SBarry Smith         }
135003249c0SBarry Smith       }
136003249c0SBarry Smith       if (!diagonly) rows[cnt++] = i;
137181dd334SBarry Smith       else drows[dcnt++] = i - rstart;
1389566063dSJacob Faibussowitsch       PetscCall(MatRestoreRow(pc->mat, i, &nz, &cols, &values));
139df826632SBarry Smith     }
140ca320bd4SBarry Smith 
14126283091SBarry Smith     /* create PetscLayout for non-diagonal rows on each process */
1429566063dSJacob Faibussowitsch     PetscCall(PetscLayoutCreate(comm, &map));
1439566063dSJacob Faibussowitsch     PetscCall(PetscLayoutSetLocalSize(map, cnt));
1449566063dSJacob Faibussowitsch     PetscCall(PetscLayoutSetBlockSize(map, 1));
1459566063dSJacob Faibussowitsch     PetscCall(PetscLayoutSetUp(map));
14669ff637eSBarry Smith     nrstart = map->rstart;
14769ff637eSBarry Smith     nrend   = map->rend;
148df826632SBarry Smith 
14926283091SBarry Smith     /* create PetscLayout for load-balanced non-diagonal rows on each process */
1509566063dSJacob Faibussowitsch     PetscCall(PetscLayoutCreate(comm, &nmap));
1511c2dc1cbSBarry Smith     PetscCall(MPIU_Allreduce(&cnt, &ncnt, 1, MPIU_INT, MPI_SUM, comm));
1529566063dSJacob Faibussowitsch     PetscCall(PetscLayoutSetSize(nmap, ncnt));
1539566063dSJacob Faibussowitsch     PetscCall(PetscLayoutSetBlockSize(nmap, 1));
1549566063dSJacob Faibussowitsch     PetscCall(PetscLayoutSetUp(nmap));
155df826632SBarry Smith 
1569566063dSJacob Faibussowitsch     PetscCall(MatGetSize(pc->pmat, &NN, NULL));
15763a3b9bcSJacob Faibussowitsch     PetscCall(PetscInfo(pc, "Number of diagonal rows eliminated %" PetscInt_FMT ", percentage eliminated %g\n", NN - ncnt, (double)(((PetscReal)(NN - ncnt)) / ((PetscReal)(NN)))));
158003249c0SBarry Smith 
159003249c0SBarry Smith     if (size > 1) {
160ca320bd4SBarry Smith       /*
16169ff637eSBarry Smith         the following block of code assumes MPI can send messages to self, which is not supported for MPI-uni hence we need to handle
16269ff637eSBarry Smith         the size 1 case as a special case
16369ff637eSBarry Smith 
164ca320bd4SBarry Smith        this code is taken from VecScatterCreate_PtoS()
165ca320bd4SBarry Smith        Determines what rows need to be moved where to
166ca320bd4SBarry Smith        load balance the non-diagonal rows
167ca320bd4SBarry Smith        */
168df826632SBarry Smith       /*  count number of contributors to each processor */
1699566063dSJacob Faibussowitsch       PetscCall(PetscMalloc2(size, &sizes, cnt, &owner));
1709566063dSJacob Faibussowitsch       PetscCall(PetscArrayzero(sizes, size));
171df826632SBarry Smith       j      = 0;
172df826632SBarry Smith       nsends = 0;
17369ff637eSBarry Smith       for (i = nrstart; i < nrend; i++) {
174df826632SBarry Smith         if (i < nmap->range[j]) j = 0;
175df826632SBarry Smith         for (; j < size; j++) {
176df826632SBarry Smith           if (i < nmap->range[j + 1]) {
17776ec1555SBarry Smith             if (!sizes[j]++) nsends++;
17869ff637eSBarry Smith             owner[i - nrstart] = j;
179df826632SBarry Smith             break;
180df826632SBarry Smith           }
181df826632SBarry Smith         }
182df826632SBarry Smith       }
183df826632SBarry Smith       /* inform other processors of number of messages and max length*/
1849566063dSJacob Faibussowitsch       PetscCall(PetscGatherNumberOfMessages(comm, NULL, sizes, &nrecvs));
1859566063dSJacob Faibussowitsch       PetscCall(PetscGatherMessageLengths(comm, nsends, nrecvs, sizes, &onodes1, &olengths1));
1869566063dSJacob Faibussowitsch       PetscCall(PetscSortMPIIntWithArray(nrecvs, onodes1, olengths1));
1879371c9d4SSatish Balay       recvtotal = 0;
1889371c9d4SSatish Balay       for (i = 0; i < nrecvs; i++) recvtotal += olengths1[i];
189df826632SBarry Smith 
190df826632SBarry Smith       /* post receives:  rvalues - rows I will own; count - nu */
1919566063dSJacob Faibussowitsch       PetscCall(PetscMalloc3(recvtotal, &rvalues, nrecvs, &source, nrecvs, &recv_waits));
192df826632SBarry Smith       count = 0;
193df826632SBarry Smith       for (i = 0; i < nrecvs; i++) {
1949566063dSJacob Faibussowitsch         PetscCallMPI(MPI_Irecv((rvalues + count), olengths1[i], MPIU_INT, onodes1[i], tag, comm, recv_waits + i));
195df826632SBarry Smith         count += olengths1[i];
196df826632SBarry Smith       }
197df826632SBarry Smith 
198df826632SBarry Smith       /* do sends:
199df826632SBarry Smith        1) starts[i] gives the starting index in svalues for stuff going to
200df826632SBarry Smith        the ith processor
201df826632SBarry Smith        */
2029566063dSJacob Faibussowitsch       PetscCall(PetscMalloc3(cnt, &svalues, nsends, &send_waits, size, &starts));
203df826632SBarry Smith       starts[0] = 0;
20476ec1555SBarry Smith       for (i = 1; i < size; i++) starts[i] = starts[i - 1] + sizes[i - 1];
2052fa5cd67SKarl Rupp       for (i = 0; i < cnt; i++) svalues[starts[owner[i]]++] = rows[i];
20669ff637eSBarry Smith       for (i = 0; i < cnt; i++) rows[i] = rows[i] - nrstart;
207181dd334SBarry Smith       red->drows = drows;
208181dd334SBarry Smith       red->dcnt  = dcnt;
2099566063dSJacob Faibussowitsch       PetscCall(PetscFree(rows));
210181dd334SBarry Smith 
211df826632SBarry Smith       starts[0] = 0;
21276ec1555SBarry Smith       for (i = 1; i < size; i++) starts[i] = starts[i - 1] + sizes[i - 1];
213df826632SBarry Smith       count = 0;
214df826632SBarry Smith       for (i = 0; i < size; i++) {
21548a46eb9SPierre Jolivet         if (sizes[i]) PetscCallMPI(MPI_Isend(svalues + starts[i], sizes[i], MPIU_INT, i, tag, comm, send_waits + count++));
216df826632SBarry Smith       }
217df826632SBarry Smith 
218df826632SBarry Smith       /*  wait on receives */
219df826632SBarry Smith       count = nrecvs;
220df826632SBarry Smith       slen  = 0;
221df826632SBarry Smith       while (count) {
2229566063dSJacob Faibussowitsch         PetscCallMPI(MPI_Waitany(nrecvs, recv_waits, &imdex, &recv_status));
223df826632SBarry Smith         /* unpack receives into our local space */
2249566063dSJacob Faibussowitsch         PetscCallMPI(MPI_Get_count(&recv_status, MPIU_INT, &n));
225df826632SBarry Smith         slen += n;
226df826632SBarry Smith         count--;
227df826632SBarry Smith       }
22863a3b9bcSJacob Faibussowitsch       PetscCheck(slen == recvtotal, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Total message lengths %" PetscInt_FMT " not expected %" PetscInt_FMT, slen, recvtotal);
2299566063dSJacob Faibussowitsch       PetscCall(ISCreateGeneral(comm, slen, rvalues, PETSC_COPY_VALUES, &red->is));
230911f9fe8SBarry Smith 
231003249c0SBarry Smith       /* free all work space */
2329566063dSJacob Faibussowitsch       PetscCall(PetscFree(olengths1));
2339566063dSJacob Faibussowitsch       PetscCall(PetscFree(onodes1));
2349566063dSJacob Faibussowitsch       PetscCall(PetscFree3(rvalues, source, recv_waits));
2359566063dSJacob Faibussowitsch       PetscCall(PetscFree2(sizes, owner));
236ca320bd4SBarry Smith       if (nsends) { /* wait on sends */
2379566063dSJacob Faibussowitsch         PetscCall(PetscMalloc1(nsends, &send_status));
2389566063dSJacob Faibussowitsch         PetscCallMPI(MPI_Waitall(nsends, send_waits, send_status));
2399566063dSJacob Faibussowitsch         PetscCall(PetscFree(send_status));
240df826632SBarry Smith       }
2419566063dSJacob Faibussowitsch       PetscCall(PetscFree3(svalues, send_waits, starts));
242003249c0SBarry Smith     } else {
2439566063dSJacob Faibussowitsch       PetscCall(ISCreateGeneral(comm, cnt, rows, PETSC_OWN_POINTER, &red->is));
244003249c0SBarry Smith       red->drows = drows;
245003249c0SBarry Smith       red->dcnt  = dcnt;
246003249c0SBarry Smith       slen       = cnt;
247003249c0SBarry Smith     }
2489566063dSJacob Faibussowitsch     PetscCall(PetscLayoutDestroy(&map));
249df826632SBarry Smith 
2509566063dSJacob Faibussowitsch     PetscCall(VecCreateMPI(comm, slen, PETSC_DETERMINE, &red->b));
2519566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(red->b, &red->x));
2529566063dSJacob Faibussowitsch     PetscCall(MatCreateVecs(pc->pmat, &tvec, NULL));
2539566063dSJacob Faibussowitsch     PetscCall(VecScatterCreate(tvec, red->is, red->b, NULL, &red->scatter));
25469ff637eSBarry Smith 
25569ff637eSBarry Smith     /* Map the PCFIELDSPLIT fields to redistributed KSP */
25669ff637eSBarry Smith     PetscCall(KSPGetPC(red->ksp, &ipc));
25769ff637eSBarry Smith     PetscCall(PetscObjectQueryFunction((PetscObject)ipc, "PCFieldSplitSetIS_C", &fptr));
25869ff637eSBarry Smith     if (fptr && *next) {
25969ff637eSBarry Smith       PetscScalar       *atvec;
26069ff637eSBarry Smith       const PetscScalar *ab;
26169ff637eSBarry Smith       PetscInt           primes[] = {2, 3, 5, 7, 11, 13, 17, 19};
26269ff637eSBarry Smith       PetscInt           cnt      = 0;
26369ff637eSBarry Smith 
26469ff637eSBarry Smith       PetscCheck(red->nsplits <= (PetscInt)PETSC_STATIC_ARRAY_LENGTH(primes), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "No support for this many fields");
26569ff637eSBarry Smith       PetscCall(VecSet(tvec, 1.0));
26669ff637eSBarry Smith       PetscCall(VecGetArray(tvec, &atvec));
26769ff637eSBarry Smith 
26869ff637eSBarry Smith       while (*next) {
26969ff637eSBarry Smith         const PetscInt *indices;
27069ff637eSBarry Smith         PetscInt        n;
27169ff637eSBarry Smith 
27269ff637eSBarry Smith         PetscCall(ISGetIndices((*next)->is, &indices));
27369ff637eSBarry Smith         PetscCall(ISGetLocalSize((*next)->is, &n));
27469ff637eSBarry Smith         for (PetscInt i = 0; i < n; i++) atvec[indices[i] - rstart] *= primes[cnt];
27569ff637eSBarry Smith         PetscCall(ISRestoreIndices((*next)->is, &indices));
27669ff637eSBarry Smith         cnt++;
27769ff637eSBarry Smith         next = &(*next)->next;
27869ff637eSBarry Smith       }
27969ff637eSBarry Smith       PetscCall(VecRestoreArray(tvec, &atvec));
28069ff637eSBarry Smith       PetscCall(VecScatterBegin(red->scatter, tvec, red->b, INSERT_VALUES, SCATTER_FORWARD));
28169ff637eSBarry Smith       PetscCall(VecScatterEnd(red->scatter, tvec, red->b, INSERT_VALUES, SCATTER_FORWARD));
28269ff637eSBarry Smith       cnt = 0;
28369ff637eSBarry Smith       PetscCall(VecGetArrayRead(red->b, &ab));
28469ff637eSBarry Smith       next = &red->splitlinks;
28569ff637eSBarry Smith       while (*next) {
28669ff637eSBarry Smith         PetscInt  n = 0;
28769ff637eSBarry Smith         PetscInt *indices;
28869ff637eSBarry Smith         IS        ris;
28969ff637eSBarry Smith 
29069ff637eSBarry Smith         for (PetscInt i = 0; i < nmap->rend - nmap->rstart; i++) {
29169ff637eSBarry Smith           if (!(((PetscInt)PetscRealPart(ab[i])) % primes[cnt])) n++;
29269ff637eSBarry Smith         }
29369ff637eSBarry Smith         PetscCall(PetscMalloc1(n, &indices));
29469ff637eSBarry Smith         n = 0;
29569ff637eSBarry Smith         for (PetscInt i = 0; i < nmap->rend - nmap->rstart; i++) {
29669ff637eSBarry Smith           if (!(((PetscInt)PetscRealPart(ab[i])) % primes[cnt])) indices[n++] = i + nmap->rstart;
29769ff637eSBarry Smith         }
29869ff637eSBarry Smith         PetscCall(ISCreateGeneral(comm, n, indices, PETSC_OWN_POINTER, &ris));
29969ff637eSBarry Smith         PetscCall(PCFieldSplitSetIS(ipc, (*next)->splitname, ris));
30069ff637eSBarry Smith 
30169ff637eSBarry Smith         PetscCall(ISDestroy(&ris));
30269ff637eSBarry Smith         cnt++;
30369ff637eSBarry Smith         next = &(*next)->next;
30469ff637eSBarry Smith       }
30569ff637eSBarry Smith       PetscCall(VecRestoreArrayRead(red->b, &ab));
30669ff637eSBarry Smith     }
3079566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&tvec));
3089566063dSJacob Faibussowitsch     PetscCall(MatCreateSubMatrix(pc->pmat, red->is, red->is, MAT_INITIAL_MATRIX, &tmat));
3099566063dSJacob Faibussowitsch     PetscCall(KSPSetOperators(red->ksp, tmat, tmat));
3109566063dSJacob Faibussowitsch     PetscCall(MatDestroy(&tmat));
31169ff637eSBarry Smith     PetscCall(PetscLayoutDestroy(&nmap));
3121d805cfdSBarry Smith   }
313181dd334SBarry Smith 
314181dd334SBarry Smith   /* get diagonal portion of matrix */
3159566063dSJacob Faibussowitsch   PetscCall(PetscFree(red->diag));
3169566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(red->dcnt, &red->diag));
3179566063dSJacob Faibussowitsch   PetscCall(MatCreateVecs(pc->pmat, &diag, NULL));
3189566063dSJacob Faibussowitsch   PetscCall(MatGetDiagonal(pc->pmat, diag));
3199566063dSJacob Faibussowitsch   PetscCall(VecGetArrayRead(diag, &d));
32087b47708SBarry Smith   for (i = 0; i < red->dcnt; i++) {
32187b47708SBarry Smith     if (d[red->drows[i]] != 0) red->diag[i] = 1.0 / d[red->drows[i]];
32287b47708SBarry Smith     else {
32387b47708SBarry Smith       red->zerodiag = PETSC_TRUE;
32487b47708SBarry Smith       red->diag[i]  = 0.0;
32587b47708SBarry Smith     }
32687b47708SBarry Smith   }
3279566063dSJacob Faibussowitsch   PetscCall(VecRestoreArrayRead(diag, &d));
3289566063dSJacob Faibussowitsch   PetscCall(VecDestroy(&diag));
3299566063dSJacob Faibussowitsch   PetscCall(KSPSetUp(red->ksp));
3303ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
331df826632SBarry Smith }
332df826632SBarry Smith 
333d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCApply_Redistribute(PC pc, Vec b, Vec x)
334d71ae5a4SJacob Faibussowitsch {
335df826632SBarry Smith   PC_Redistribute   *red   = (PC_Redistribute *)pc->data;
336181dd334SBarry Smith   PetscInt           dcnt  = red->dcnt, i;
337181dd334SBarry Smith   const PetscInt    *drows = red->drows;
338181dd334SBarry Smith   PetscScalar       *xwork;
339181dd334SBarry Smith   const PetscScalar *bwork, *diag = red->diag;
340*3ae97395SBarry Smith   PetscBool          nonzero_guess;
341df826632SBarry Smith 
342df826632SBarry Smith   PetscFunctionBegin;
34348a46eb9SPierre Jolivet   if (!red->work) PetscCall(VecDuplicate(b, &red->work));
344*3ae97395SBarry Smith   PetscCall(KSPGetInitialGuessNonzero(red->ksp, &nonzero_guess));
345*3ae97395SBarry Smith   if (nonzero_guess) {
346*3ae97395SBarry Smith     PetscCall(VecScatterBegin(red->scatter, x, red->x, INSERT_VALUES, SCATTER_FORWARD));
347*3ae97395SBarry Smith     PetscCall(VecScatterEnd(red->scatter, x, red->x, INSERT_VALUES, SCATTER_FORWARD));
348*3ae97395SBarry Smith   }
349*3ae97395SBarry Smith 
350181dd334SBarry Smith   /* compute the rows of solution that have diagonal entries only */
3519566063dSJacob Faibussowitsch   PetscCall(VecSet(x, 0.0)); /* x = diag(A)^{-1} b */
3529566063dSJacob Faibussowitsch   PetscCall(VecGetArray(x, &xwork));
3539566063dSJacob Faibussowitsch   PetscCall(VecGetArrayRead(b, &bwork));
35487b47708SBarry Smith   if (red->zerodiag) {
35587b47708SBarry Smith     for (i = 0; i < dcnt; i++) {
35687b47708SBarry Smith       if (diag[i] == 0.0 && bwork[drows[i]] != 0.0) {
35787b47708SBarry Smith         PetscCheck(!pc->erroriffailure, PETSC_COMM_SELF, PETSC_ERR_CONV_FAILED, "Linear system is inconsistent, zero matrix row but nonzero right hand side");
3589d3446b2SPierre Jolivet         PetscCall(PetscInfo(pc, "Linear system is inconsistent, zero matrix row but nonzero right hand side\n"));
35987b47708SBarry Smith         PetscCall(VecSetInf(x));
36087b47708SBarry Smith         pc->failedreasonrank = PC_INCONSISTENT_RHS;
36187b47708SBarry Smith       }
36287b47708SBarry Smith     }
36387b47708SBarry Smith   }
3642fa5cd67SKarl Rupp   for (i = 0; i < dcnt; i++) xwork[drows[i]] = diag[i] * bwork[drows[i]];
3659566063dSJacob Faibussowitsch   PetscCall(PetscLogFlops(dcnt));
3669566063dSJacob Faibussowitsch   PetscCall(VecRestoreArray(red->work, &xwork));
3679566063dSJacob Faibussowitsch   PetscCall(VecRestoreArrayRead(b, &bwork));
368181dd334SBarry Smith   /* update the right hand side for the reduced system with diagonal rows (and corresponding columns) removed */
3699566063dSJacob Faibussowitsch   PetscCall(MatMult(pc->pmat, x, red->work));
3709566063dSJacob Faibussowitsch   PetscCall(VecAYPX(red->work, -1.0, b)); /* red->work = b - A x */
371181dd334SBarry Smith 
3729566063dSJacob Faibussowitsch   PetscCall(VecScatterBegin(red->scatter, red->work, red->b, INSERT_VALUES, SCATTER_FORWARD));
3739566063dSJacob Faibussowitsch   PetscCall(VecScatterEnd(red->scatter, red->work, red->b, INSERT_VALUES, SCATTER_FORWARD));
3749566063dSJacob Faibussowitsch   PetscCall(KSPSolve(red->ksp, red->b, red->x));
3759566063dSJacob Faibussowitsch   PetscCall(KSPCheckSolve(red->ksp, pc, red->x));
3769566063dSJacob Faibussowitsch   PetscCall(VecScatterBegin(red->scatter, red->x, x, INSERT_VALUES, SCATTER_REVERSE));
3779566063dSJacob Faibussowitsch   PetscCall(VecScatterEnd(red->scatter, red->x, x, INSERT_VALUES, SCATTER_REVERSE));
3783ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
379df826632SBarry Smith }
380df826632SBarry Smith 
381d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCDestroy_Redistribute(PC pc)
382d71ae5a4SJacob Faibussowitsch {
383df826632SBarry Smith   PC_Redistribute  *red  = (PC_Redistribute *)pc->data;
38469ff637eSBarry Smith   PC_FieldSplitLink next = red->splitlinks;
385df826632SBarry Smith 
386df826632SBarry Smith   PetscFunctionBegin;
38769ff637eSBarry Smith   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetIS_C", NULL));
38869ff637eSBarry Smith 
38969ff637eSBarry Smith   while (next) {
39069ff637eSBarry Smith     PC_FieldSplitLink ilink;
39169ff637eSBarry Smith     PetscCall(PetscFree(next->splitname));
39269ff637eSBarry Smith     PetscCall(ISDestroy(&next->is));
39369ff637eSBarry Smith     ilink = next;
39469ff637eSBarry Smith     next  = next->next;
39569ff637eSBarry Smith     PetscCall(PetscFree(ilink));
39669ff637eSBarry Smith   }
3979566063dSJacob Faibussowitsch   PetscCall(VecScatterDestroy(&red->scatter));
3989566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&red->is));
3999566063dSJacob Faibussowitsch   PetscCall(VecDestroy(&red->b));
4009566063dSJacob Faibussowitsch   PetscCall(VecDestroy(&red->x));
4019566063dSJacob Faibussowitsch   PetscCall(KSPDestroy(&red->ksp));
4029566063dSJacob Faibussowitsch   PetscCall(VecDestroy(&red->work));
4039566063dSJacob Faibussowitsch   PetscCall(PetscFree(red->drows));
4049566063dSJacob Faibussowitsch   PetscCall(PetscFree(red->diag));
4059566063dSJacob Faibussowitsch   PetscCall(PetscFree(pc->data));
4063ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
407df826632SBarry Smith }
408df826632SBarry Smith 
409d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCSetFromOptions_Redistribute(PC pc, PetscOptionItems *PetscOptionsObject)
410d71ae5a4SJacob Faibussowitsch {
411df826632SBarry Smith   PC_Redistribute *red = (PC_Redistribute *)pc->data;
412df826632SBarry Smith 
413df826632SBarry Smith   PetscFunctionBegin;
4149566063dSJacob Faibussowitsch   PetscCall(KSPSetFromOptions(red->ksp));
4153ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
416df826632SBarry Smith }
417df826632SBarry Smith 
4185e7ef714SBarry Smith /*@
419f1580f4eSBarry Smith   PCRedistributeGetKSP - Gets the `KSP` created by the `PCREDISTRIBUTE`
4205e7ef714SBarry Smith 
4215e7ef714SBarry Smith   Not Collective
4225e7ef714SBarry Smith 
4235e7ef714SBarry Smith   Input Parameter:
4245e7ef714SBarry Smith . pc - the preconditioner context
4255e7ef714SBarry Smith 
4265e7ef714SBarry Smith   Output Parameter:
427f1580f4eSBarry Smith . innerksp - the inner `KSP`
4285e7ef714SBarry Smith 
4295e7ef714SBarry Smith   Level: advanced
4305e7ef714SBarry Smith 
431f1580f4eSBarry Smith .seealso: `KSP`, `PCREDISTRIBUTE`
4325e7ef714SBarry Smith @*/
433d71ae5a4SJacob Faibussowitsch PetscErrorCode PCRedistributeGetKSP(PC pc, KSP *innerksp)
434d71ae5a4SJacob Faibussowitsch {
4355e7ef714SBarry Smith   PC_Redistribute *red = (PC_Redistribute *)pc->data;
4365e7ef714SBarry Smith 
4375e7ef714SBarry Smith   PetscFunctionBegin;
4385e7ef714SBarry Smith   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
4394f572ea9SToby Isaac   PetscAssertPointer(innerksp, 2);
4405e7ef714SBarry Smith   *innerksp = red->ksp;
4413ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
4425e7ef714SBarry Smith }
4435e7ef714SBarry Smith 
444df826632SBarry Smith /*MC
4457caa67d9SBarry Smith      PCREDISTRIBUTE - Redistributes a matrix for load balancing, removing the rows (and the corresponding columns) that only have a diagonal entry and then
446f1580f4eSBarry Smith      applies a `KSP` to that new smaller matrix
447df826632SBarry Smith 
4487df6f684SBarry Smith      Level: intermediate
4497df6f684SBarry Smith 
45095452b02SPatrick Sanan      Notes:
451f1580f4eSBarry Smith      Options for the redistribute `KSP` and `PC` with the options database prefix -redistribute_
452f1580f4eSBarry Smith 
4537df6f684SBarry Smith      Usually run this with `-ksp_type preonly`
454181dd334SBarry Smith 
4557df6f684SBarry Smith      If you have used `MatZeroRows()` to eliminate (for example, Dirichlet) boundary conditions for a symmetric problem then you can use, for example, `-ksp_type preonly
4567df6f684SBarry Smith      -pc_type redistribute -redistribute_ksp_type cg -redistribute_pc_type bjacobi -redistribute_sub_pc_type icc` to take advantage of the symmetry.
457181dd334SBarry Smith 
45869ff637eSBarry Smith      Supports the function `PCFieldSplitSetIS()`; passs the appropriate reduced field indices to an inner `PCFIELDSPLIT`, set with, for example
45969ff637eSBarry Smith      `-ksp_type preonly -pc_type redistribute -redistribute_pc_type fieldsplit. Does not support the `PCFIELDSPLIT` options database keys.
46069ff637eSBarry Smith 
4617df6f684SBarry Smith      This does NOT call a partitioner to reorder rows to lower communication; the ordering of the rows in the original matrix and redistributed matrix is the same. Rows are moved
4627df6f684SBarry Smith      between MPI processes inside the preconditioner to balance the number of rows on each process.
4632dfef595SBarry Smith 
464f1580f4eSBarry Smith      Developer Note:
46595452b02SPatrick Sanan      Should add an option to this preconditioner to use a partitioner to redistribute the rows to lower communication.
4662dfef595SBarry Smith 
46769ff637eSBarry Smith .seealso: `PCCreate()`, `PCSetType()`, `PCType`, `PCRedistributeGetKSP()`, `MatZeroRows()`, `PCFieldSplitSetIS()`, `PCFIELDSPLIT`
468df826632SBarry Smith M*/
469df826632SBarry Smith 
470d71ae5a4SJacob Faibussowitsch PETSC_EXTERN PetscErrorCode PCCreate_Redistribute(PC pc)
471d71ae5a4SJacob Faibussowitsch {
472df826632SBarry Smith   PC_Redistribute *red;
473911f9fe8SBarry Smith   const char      *prefix;
474df826632SBarry Smith 
475df826632SBarry Smith   PetscFunctionBegin;
4764dfa11a4SJacob Faibussowitsch   PetscCall(PetscNew(&red));
477df826632SBarry Smith   pc->data = (void *)red;
478df826632SBarry Smith 
479df826632SBarry Smith   pc->ops->apply          = PCApply_Redistribute;
4800a545947SLisandro Dalcin   pc->ops->applytranspose = NULL;
481df826632SBarry Smith   pc->ops->setup          = PCSetUp_Redistribute;
482df826632SBarry Smith   pc->ops->destroy        = PCDestroy_Redistribute;
483df826632SBarry Smith   pc->ops->setfromoptions = PCSetFromOptions_Redistribute;
484df826632SBarry Smith   pc->ops->view           = PCView_Redistribute;
485911f9fe8SBarry Smith 
4869566063dSJacob Faibussowitsch   PetscCall(KSPCreate(PetscObjectComm((PetscObject)pc), &red->ksp));
4873821be0aSBarry Smith   PetscCall(KSPSetNestLevel(red->ksp, pc->kspnestlevel));
4889566063dSJacob Faibussowitsch   PetscCall(KSPSetErrorIfNotConverged(red->ksp, pc->erroriffailure));
4899566063dSJacob Faibussowitsch   PetscCall(PetscObjectIncrementTabLevel((PetscObject)red->ksp, (PetscObject)pc, 1));
4909566063dSJacob Faibussowitsch   PetscCall(PCGetOptionsPrefix(pc, &prefix));
4919566063dSJacob Faibussowitsch   PetscCall(KSPSetOptionsPrefix(red->ksp, prefix));
4929566063dSJacob Faibussowitsch   PetscCall(KSPAppendOptionsPrefix(red->ksp, "redistribute_"));
49369ff637eSBarry Smith   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetIS_C", PCFieldSplitSetIS_Redistribute));
4943ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
495df826632SBarry Smith }
496