xref: /petsc/src/ksp/pc/impls/redistribute/redistribute.c (revision 562efe2ef922487c6beae96ba39e19afd4eefbe6)
1df826632SBarry Smith /*
2df826632SBarry Smith   This file defines a "solve the problem redistributely on each subgroup of processor" preconditioner.
3df826632SBarry Smith */
4af0996ceSBarry Smith #include <petsc/private/pcimpl.h> /*I "petscksp.h" I*/
5c6db04a5SJed Brown #include <petscksp.h>
6df826632SBarry Smith 
769ff637eSBarry Smith typedef struct _PC_FieldSplitLink *PC_FieldSplitLink;
869ff637eSBarry Smith struct _PC_FieldSplitLink {
969ff637eSBarry Smith   char             *splitname;
1069ff637eSBarry Smith   IS                is;
1169ff637eSBarry Smith   PC_FieldSplitLink next, previous;
1269ff637eSBarry Smith };
1369ff637eSBarry Smith 
14df826632SBarry Smith typedef struct {
15df826632SBarry Smith   KSP          ksp;
16df826632SBarry Smith   Vec          x, b;
17df826632SBarry Smith   VecScatter   scatter;
18911f9fe8SBarry Smith   IS           is;
19181dd334SBarry Smith   PetscInt     dcnt, *drows; /* these are the local rows that have only diagonal entry */
20181dd334SBarry Smith   PetscScalar *diag;
21181dd334SBarry Smith   Vec          work;
2287b47708SBarry Smith   PetscBool    zerodiag;
2369ff637eSBarry Smith 
2469ff637eSBarry Smith   PetscInt          nsplits;
2569ff637eSBarry Smith   PC_FieldSplitLink splitlinks;
26df826632SBarry Smith } PC_Redistribute;
27df826632SBarry Smith 
2869ff637eSBarry Smith static PetscErrorCode PCFieldSplitSetIS_Redistribute(PC pc, const char splitname[], IS is)
2969ff637eSBarry Smith {
3069ff637eSBarry Smith   PC_Redistribute   *red  = (PC_Redistribute *)pc->data;
3169ff637eSBarry Smith   PC_FieldSplitLink *next = &red->splitlinks;
3269ff637eSBarry Smith 
3369ff637eSBarry Smith   PetscFunctionBegin;
3469ff637eSBarry Smith   while (*next) next = &(*next)->next;
3569ff637eSBarry Smith   PetscCall(PetscNew(next));
3669ff637eSBarry Smith   if (splitname) {
3769ff637eSBarry Smith     PetscCall(PetscStrallocpy(splitname, &(*next)->splitname));
3869ff637eSBarry Smith   } else {
3969ff637eSBarry Smith     PetscCall(PetscMalloc1(8, &(*next)->splitname));
4069ff637eSBarry Smith     PetscCall(PetscSNPrintf((*next)->splitname, 7, "%" PetscInt_FMT, red->nsplits++));
4169ff637eSBarry Smith   }
4269ff637eSBarry Smith   PetscCall(PetscObjectReference((PetscObject)is));
4369ff637eSBarry Smith   PetscCall(ISDestroy(&(*next)->is));
4469ff637eSBarry Smith   (*next)->is = is;
4569ff637eSBarry Smith   PetscFunctionReturn(PETSC_SUCCESS);
4669ff637eSBarry Smith }
4769ff637eSBarry Smith 
48d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCView_Redistribute(PC pc, PetscViewer viewer)
49d71ae5a4SJacob Faibussowitsch {
50df826632SBarry Smith   PC_Redistribute *red = (PC_Redistribute *)pc->data;
51ace3abfcSBarry Smith   PetscBool        iascii, isstring;
52181dd334SBarry Smith   PetscInt         ncnt, N;
53df826632SBarry Smith 
54df826632SBarry Smith   PetscFunctionBegin;
559566063dSJacob Faibussowitsch   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
569566063dSJacob Faibussowitsch   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERSTRING, &isstring));
57df826632SBarry Smith   if (iascii) {
581c2dc1cbSBarry Smith     PetscCall(MPIU_Allreduce(&red->dcnt, &ncnt, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)pc)));
599566063dSJacob Faibussowitsch     PetscCall(MatGetSize(pc->pmat, &N, NULL));
6063a3b9bcSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "    Number rows eliminated %" PetscInt_FMT " Percentage rows eliminated %g\n", ncnt, (double)(100.0 * ((PetscReal)ncnt) / ((PetscReal)N))));
619566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "  Redistribute preconditioner: \n"));
629566063dSJacob Faibussowitsch     PetscCall(KSPView(red->ksp, viewer));
63df826632SBarry Smith   } else if (isstring) {
649566063dSJacob Faibussowitsch     PetscCall(PetscViewerStringSPrintf(viewer, " Redistribute preconditioner"));
659566063dSJacob Faibussowitsch     PetscCall(KSPView(red->ksp, viewer));
6611aeaf0aSBarry Smith   }
673ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
68df826632SBarry Smith }
69df826632SBarry Smith 
70d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCSetUp_Redistribute(PC pc)
71d71ae5a4SJacob Faibussowitsch {
72df826632SBarry Smith   PC_Redistribute         *red = (PC_Redistribute *)pc->data;
73df826632SBarry Smith   MPI_Comm                 comm;
7469ff637eSBarry Smith   PetscInt                 rstart, rend, nrstart, nrend, i, nz, cnt, *rows, ncnt, dcnt, *drows;
7526283091SBarry Smith   PetscLayout              map, nmap;
76ec4bef21SJose E. Roman   PetscMPIInt              size, tag, n;
77ec4bef21SJose E. Roman   PETSC_UNUSED PetscMPIInt imdex;
780298fd71SBarry Smith   PetscInt                *source = NULL;
7976ec1555SBarry Smith   PetscMPIInt             *sizes  = NULL, nrecvs;
80df826632SBarry Smith   PetscInt                 j, nsends;
810298fd71SBarry Smith   PetscInt                *owner = NULL, *starts = NULL, count, slen;
82e8dd6687SHong Zhang   PetscInt                *rvalues, *svalues, recvtotal;
83df826632SBarry Smith   PetscMPIInt             *onodes1, *olengths1;
840298fd71SBarry Smith   MPI_Request             *send_waits = NULL, *recv_waits = NULL;
85df826632SBarry Smith   MPI_Status               recv_status, *send_status;
86181dd334SBarry Smith   Vec                      tvec, diag;
87ca320bd4SBarry Smith   Mat                      tmat;
88003249c0SBarry Smith   const PetscScalar       *d, *values;
89003249c0SBarry Smith   const PetscInt          *cols;
9069ff637eSBarry Smith   PC_FieldSplitLink       *next = &red->splitlinks;
91df826632SBarry Smith 
92df826632SBarry Smith   PetscFunctionBegin;
93dc9360f3SBarry Smith   if (pc->setupcalled) {
9469ff637eSBarry 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");
959566063dSJacob Faibussowitsch     PetscCall(KSPGetOperators(red->ksp, NULL, &tmat));
969566063dSJacob Faibussowitsch     PetscCall(MatCreateSubMatrix(pc->pmat, red->is, red->is, MAT_REUSE_MATRIX, &tmat));
979566063dSJacob Faibussowitsch     PetscCall(KSPSetOperators(red->ksp, tmat, tmat));
98dc9360f3SBarry Smith   } else {
99b862ddfaSBarry Smith     PetscInt          NN;
10069ff637eSBarry Smith     PC                ipc;
10169ff637eSBarry Smith     PetscVoidFunction fptr;
102b862ddfaSBarry Smith 
1039566063dSJacob Faibussowitsch     PetscCall(PetscObjectGetComm((PetscObject)pc, &comm));
1049566063dSJacob Faibussowitsch     PetscCallMPI(MPI_Comm_size(comm, &size));
1059566063dSJacob Faibussowitsch     PetscCall(PetscObjectGetNewTag((PetscObject)pc, &tag));
106df826632SBarry Smith 
107ca320bd4SBarry Smith     /* count non-diagonal rows on process */
1089566063dSJacob Faibussowitsch     PetscCall(MatGetOwnershipRange(pc->mat, &rstart, &rend));
109ca320bd4SBarry Smith     cnt = 0;
110df826632SBarry Smith     for (i = rstart; i < rend; i++) {
1119566063dSJacob Faibussowitsch       PetscCall(MatGetRow(pc->mat, i, &nz, &cols, &values));
112003249c0SBarry Smith       for (PetscInt j = 0; j < nz; j++) {
113003249c0SBarry Smith         if (values[j] != 0 && cols[j] != i) {
114003249c0SBarry Smith           cnt++;
115003249c0SBarry Smith           break;
116003249c0SBarry Smith         }
117003249c0SBarry Smith       }
1189566063dSJacob Faibussowitsch       PetscCall(MatRestoreRow(pc->mat, i, &nz, &cols, &values));
119df826632SBarry Smith     }
1209566063dSJacob Faibussowitsch     PetscCall(PetscMalloc1(cnt, &rows));
1219566063dSJacob Faibussowitsch     PetscCall(PetscMalloc1(rend - rstart - cnt, &drows));
122ca320bd4SBarry Smith 
123ca320bd4SBarry Smith     /* list non-diagonal rows on process */
1249371c9d4SSatish Balay     cnt  = 0;
1259371c9d4SSatish Balay     dcnt = 0;
126df826632SBarry Smith     for (i = rstart; i < rend; i++) {
127003249c0SBarry Smith       PetscBool diagonly = PETSC_TRUE;
1289566063dSJacob Faibussowitsch       PetscCall(MatGetRow(pc->mat, i, &nz, &cols, &values));
129003249c0SBarry Smith       for (PetscInt j = 0; j < nz; j++) {
130003249c0SBarry Smith         if (values[j] != 0 && cols[j] != i) {
131003249c0SBarry Smith           diagonly = PETSC_FALSE;
132003249c0SBarry Smith           break;
133003249c0SBarry Smith         }
134003249c0SBarry Smith       }
135003249c0SBarry Smith       if (!diagonly) rows[cnt++] = i;
136181dd334SBarry Smith       else drows[dcnt++] = i - rstart;
1379566063dSJacob Faibussowitsch       PetscCall(MatRestoreRow(pc->mat, i, &nz, &cols, &values));
138df826632SBarry Smith     }
139ca320bd4SBarry Smith 
14026283091SBarry Smith     /* create PetscLayout for non-diagonal rows on each process */
1419566063dSJacob Faibussowitsch     PetscCall(PetscLayoutCreate(comm, &map));
1429566063dSJacob Faibussowitsch     PetscCall(PetscLayoutSetLocalSize(map, cnt));
1439566063dSJacob Faibussowitsch     PetscCall(PetscLayoutSetBlockSize(map, 1));
1449566063dSJacob Faibussowitsch     PetscCall(PetscLayoutSetUp(map));
14569ff637eSBarry Smith     nrstart = map->rstart;
14669ff637eSBarry Smith     nrend   = map->rend;
147df826632SBarry Smith 
14826283091SBarry Smith     /* create PetscLayout for load-balanced non-diagonal rows on each process */
1499566063dSJacob Faibussowitsch     PetscCall(PetscLayoutCreate(comm, &nmap));
1501c2dc1cbSBarry Smith     PetscCall(MPIU_Allreduce(&cnt, &ncnt, 1, MPIU_INT, MPI_SUM, comm));
1519566063dSJacob Faibussowitsch     PetscCall(PetscLayoutSetSize(nmap, ncnt));
1529566063dSJacob Faibussowitsch     PetscCall(PetscLayoutSetBlockSize(nmap, 1));
1539566063dSJacob Faibussowitsch     PetscCall(PetscLayoutSetUp(nmap));
154df826632SBarry Smith 
1559566063dSJacob Faibussowitsch     PetscCall(MatGetSize(pc->pmat, &NN, NULL));
15663a3b9bcSJacob Faibussowitsch     PetscCall(PetscInfo(pc, "Number of diagonal rows eliminated %" PetscInt_FMT ", percentage eliminated %g\n", NN - ncnt, (double)(((PetscReal)(NN - ncnt)) / ((PetscReal)(NN)))));
157003249c0SBarry Smith 
158003249c0SBarry Smith     if (size > 1) {
159ca320bd4SBarry Smith       /*
16069ff637eSBarry 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
16169ff637eSBarry Smith         the size 1 case as a special case
16269ff637eSBarry Smith 
163ca320bd4SBarry Smith        this code is taken from VecScatterCreate_PtoS()
164ca320bd4SBarry Smith        Determines what rows need to be moved where to
165ca320bd4SBarry Smith        load balance the non-diagonal rows
166ca320bd4SBarry Smith        */
167df826632SBarry Smith       /*  count number of contributors to each processor */
1689566063dSJacob Faibussowitsch       PetscCall(PetscMalloc2(size, &sizes, cnt, &owner));
1699566063dSJacob Faibussowitsch       PetscCall(PetscArrayzero(sizes, size));
170df826632SBarry Smith       j      = 0;
171df826632SBarry Smith       nsends = 0;
17269ff637eSBarry Smith       for (i = nrstart; i < nrend; i++) {
173df826632SBarry Smith         if (i < nmap->range[j]) j = 0;
174df826632SBarry Smith         for (; j < size; j++) {
175df826632SBarry Smith           if (i < nmap->range[j + 1]) {
17676ec1555SBarry Smith             if (!sizes[j]++) nsends++;
17769ff637eSBarry Smith             owner[i - nrstart] = j;
178df826632SBarry Smith             break;
179df826632SBarry Smith           }
180df826632SBarry Smith         }
181df826632SBarry Smith       }
182df826632SBarry Smith       /* inform other processors of number of messages and max length*/
1839566063dSJacob Faibussowitsch       PetscCall(PetscGatherNumberOfMessages(comm, NULL, sizes, &nrecvs));
1849566063dSJacob Faibussowitsch       PetscCall(PetscGatherMessageLengths(comm, nsends, nrecvs, sizes, &onodes1, &olengths1));
1859566063dSJacob Faibussowitsch       PetscCall(PetscSortMPIIntWithArray(nrecvs, onodes1, olengths1));
1869371c9d4SSatish Balay       recvtotal = 0;
1879371c9d4SSatish Balay       for (i = 0; i < nrecvs; i++) recvtotal += olengths1[i];
188df826632SBarry Smith 
189df826632SBarry Smith       /* post receives:  rvalues - rows I will own; count - nu */
1909566063dSJacob Faibussowitsch       PetscCall(PetscMalloc3(recvtotal, &rvalues, nrecvs, &source, nrecvs, &recv_waits));
191df826632SBarry Smith       count = 0;
192df826632SBarry Smith       for (i = 0; i < nrecvs; i++) {
1939566063dSJacob Faibussowitsch         PetscCallMPI(MPI_Irecv((rvalues + count), olengths1[i], MPIU_INT, onodes1[i], tag, comm, recv_waits + i));
194df826632SBarry Smith         count += olengths1[i];
195df826632SBarry Smith       }
196df826632SBarry Smith 
197df826632SBarry Smith       /* do sends:
198df826632SBarry Smith        1) starts[i] gives the starting index in svalues for stuff going to
199df826632SBarry Smith        the ith processor
200df826632SBarry Smith        */
2019566063dSJacob Faibussowitsch       PetscCall(PetscMalloc3(cnt, &svalues, nsends, &send_waits, size, &starts));
202df826632SBarry Smith       starts[0] = 0;
20376ec1555SBarry Smith       for (i = 1; i < size; i++) starts[i] = starts[i - 1] + sizes[i - 1];
2042fa5cd67SKarl Rupp       for (i = 0; i < cnt; i++) svalues[starts[owner[i]]++] = rows[i];
20569ff637eSBarry Smith       for (i = 0; i < cnt; i++) rows[i] = rows[i] - nrstart;
206181dd334SBarry Smith       red->drows = drows;
207181dd334SBarry Smith       red->dcnt  = dcnt;
2089566063dSJacob Faibussowitsch       PetscCall(PetscFree(rows));
209181dd334SBarry Smith 
210df826632SBarry Smith       starts[0] = 0;
21176ec1555SBarry Smith       for (i = 1; i < size; i++) starts[i] = starts[i - 1] + sizes[i - 1];
212df826632SBarry Smith       count = 0;
213df826632SBarry Smith       for (i = 0; i < size; i++) {
21448a46eb9SPierre Jolivet         if (sizes[i]) PetscCallMPI(MPI_Isend(svalues + starts[i], sizes[i], MPIU_INT, i, tag, comm, send_waits + count++));
215df826632SBarry Smith       }
216df826632SBarry Smith 
217df826632SBarry Smith       /*  wait on receives */
218df826632SBarry Smith       count = nrecvs;
219df826632SBarry Smith       slen  = 0;
220df826632SBarry Smith       while (count) {
2219566063dSJacob Faibussowitsch         PetscCallMPI(MPI_Waitany(nrecvs, recv_waits, &imdex, &recv_status));
222df826632SBarry Smith         /* unpack receives into our local space */
2239566063dSJacob Faibussowitsch         PetscCallMPI(MPI_Get_count(&recv_status, MPIU_INT, &n));
224df826632SBarry Smith         slen += n;
225df826632SBarry Smith         count--;
226df826632SBarry Smith       }
22763a3b9bcSJacob Faibussowitsch       PetscCheck(slen == recvtotal, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Total message lengths %" PetscInt_FMT " not expected %" PetscInt_FMT, slen, recvtotal);
2289566063dSJacob Faibussowitsch       PetscCall(ISCreateGeneral(comm, slen, rvalues, PETSC_COPY_VALUES, &red->is));
229911f9fe8SBarry Smith 
230003249c0SBarry Smith       /* free all work space */
2319566063dSJacob Faibussowitsch       PetscCall(PetscFree(olengths1));
2329566063dSJacob Faibussowitsch       PetscCall(PetscFree(onodes1));
2339566063dSJacob Faibussowitsch       PetscCall(PetscFree3(rvalues, source, recv_waits));
2349566063dSJacob Faibussowitsch       PetscCall(PetscFree2(sizes, owner));
235ca320bd4SBarry Smith       if (nsends) { /* wait on sends */
2369566063dSJacob Faibussowitsch         PetscCall(PetscMalloc1(nsends, &send_status));
2379566063dSJacob Faibussowitsch         PetscCallMPI(MPI_Waitall(nsends, send_waits, send_status));
2389566063dSJacob Faibussowitsch         PetscCall(PetscFree(send_status));
239df826632SBarry Smith       }
2409566063dSJacob Faibussowitsch       PetscCall(PetscFree3(svalues, send_waits, starts));
241003249c0SBarry Smith     } else {
2429566063dSJacob Faibussowitsch       PetscCall(ISCreateGeneral(comm, cnt, rows, PETSC_OWN_POINTER, &red->is));
243003249c0SBarry Smith       red->drows = drows;
244003249c0SBarry Smith       red->dcnt  = dcnt;
245003249c0SBarry Smith       slen       = cnt;
246003249c0SBarry Smith     }
2479566063dSJacob Faibussowitsch     PetscCall(PetscLayoutDestroy(&map));
248df826632SBarry Smith 
2499566063dSJacob Faibussowitsch     PetscCall(VecCreateMPI(comm, slen, PETSC_DETERMINE, &red->b));
2509566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(red->b, &red->x));
2519566063dSJacob Faibussowitsch     PetscCall(MatCreateVecs(pc->pmat, &tvec, NULL));
2529566063dSJacob Faibussowitsch     PetscCall(VecScatterCreate(tvec, red->is, red->b, NULL, &red->scatter));
25369ff637eSBarry Smith 
25469ff637eSBarry Smith     /* Map the PCFIELDSPLIT fields to redistributed KSP */
25569ff637eSBarry Smith     PetscCall(KSPGetPC(red->ksp, &ipc));
25669ff637eSBarry Smith     PetscCall(PetscObjectQueryFunction((PetscObject)ipc, "PCFieldSplitSetIS_C", &fptr));
25769ff637eSBarry Smith     if (fptr && *next) {
25869ff637eSBarry Smith       PetscScalar       *atvec;
25969ff637eSBarry Smith       const PetscScalar *ab;
26069ff637eSBarry Smith       PetscInt           primes[] = {2, 3, 5, 7, 11, 13, 17, 19};
26169ff637eSBarry Smith       PetscInt           cnt      = 0;
26269ff637eSBarry Smith 
26369ff637eSBarry Smith       PetscCheck(red->nsplits <= (PetscInt)PETSC_STATIC_ARRAY_LENGTH(primes), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "No support for this many fields");
26469ff637eSBarry Smith       PetscCall(VecSet(tvec, 1.0));
26569ff637eSBarry Smith       PetscCall(VecGetArray(tvec, &atvec));
26669ff637eSBarry Smith 
26769ff637eSBarry Smith       while (*next) {
26869ff637eSBarry Smith         const PetscInt *indices;
26969ff637eSBarry Smith         PetscInt        n;
27069ff637eSBarry Smith 
27169ff637eSBarry Smith         PetscCall(ISGetIndices((*next)->is, &indices));
27269ff637eSBarry Smith         PetscCall(ISGetLocalSize((*next)->is, &n));
27369ff637eSBarry Smith         for (PetscInt i = 0; i < n; i++) atvec[indices[i] - rstart] *= primes[cnt];
27469ff637eSBarry Smith         PetscCall(ISRestoreIndices((*next)->is, &indices));
27569ff637eSBarry Smith         cnt++;
27669ff637eSBarry Smith         next = &(*next)->next;
27769ff637eSBarry Smith       }
27869ff637eSBarry Smith       PetscCall(VecRestoreArray(tvec, &atvec));
27969ff637eSBarry Smith       PetscCall(VecScatterBegin(red->scatter, tvec, red->b, INSERT_VALUES, SCATTER_FORWARD));
28069ff637eSBarry Smith       PetscCall(VecScatterEnd(red->scatter, tvec, red->b, INSERT_VALUES, SCATTER_FORWARD));
28169ff637eSBarry Smith       cnt = 0;
28269ff637eSBarry Smith       PetscCall(VecGetArrayRead(red->b, &ab));
28369ff637eSBarry Smith       next = &red->splitlinks;
28469ff637eSBarry Smith       while (*next) {
28569ff637eSBarry Smith         PetscInt  n = 0;
28669ff637eSBarry Smith         PetscInt *indices;
28769ff637eSBarry Smith         IS        ris;
28869ff637eSBarry Smith 
28969ff637eSBarry Smith         for (PetscInt i = 0; i < nmap->rend - nmap->rstart; i++) {
29069ff637eSBarry Smith           if (!(((PetscInt)PetscRealPart(ab[i])) % primes[cnt])) n++;
29169ff637eSBarry Smith         }
29269ff637eSBarry Smith         PetscCall(PetscMalloc1(n, &indices));
29369ff637eSBarry Smith         n = 0;
29469ff637eSBarry Smith         for (PetscInt i = 0; i < nmap->rend - nmap->rstart; i++) {
29569ff637eSBarry Smith           if (!(((PetscInt)PetscRealPart(ab[i])) % primes[cnt])) indices[n++] = i + nmap->rstart;
29669ff637eSBarry Smith         }
29769ff637eSBarry Smith         PetscCall(ISCreateGeneral(comm, n, indices, PETSC_OWN_POINTER, &ris));
29869ff637eSBarry Smith         PetscCall(PCFieldSplitSetIS(ipc, (*next)->splitname, ris));
29969ff637eSBarry Smith 
30069ff637eSBarry Smith         PetscCall(ISDestroy(&ris));
30169ff637eSBarry Smith         cnt++;
30269ff637eSBarry Smith         next = &(*next)->next;
30369ff637eSBarry Smith       }
30469ff637eSBarry Smith       PetscCall(VecRestoreArrayRead(red->b, &ab));
30569ff637eSBarry Smith     }
3069566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&tvec));
3079566063dSJacob Faibussowitsch     PetscCall(MatCreateSubMatrix(pc->pmat, red->is, red->is, MAT_INITIAL_MATRIX, &tmat));
3089566063dSJacob Faibussowitsch     PetscCall(KSPSetOperators(red->ksp, tmat, tmat));
3099566063dSJacob Faibussowitsch     PetscCall(MatDestroy(&tmat));
31069ff637eSBarry Smith     PetscCall(PetscLayoutDestroy(&nmap));
3111d805cfdSBarry Smith   }
312181dd334SBarry Smith 
313181dd334SBarry Smith   /* get diagonal portion of matrix */
3149566063dSJacob Faibussowitsch   PetscCall(PetscFree(red->diag));
3159566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(red->dcnt, &red->diag));
3169566063dSJacob Faibussowitsch   PetscCall(MatCreateVecs(pc->pmat, &diag, NULL));
3179566063dSJacob Faibussowitsch   PetscCall(MatGetDiagonal(pc->pmat, diag));
3189566063dSJacob Faibussowitsch   PetscCall(VecGetArrayRead(diag, &d));
31987b47708SBarry Smith   for (i = 0; i < red->dcnt; i++) {
32087b47708SBarry Smith     if (d[red->drows[i]] != 0) red->diag[i] = 1.0 / d[red->drows[i]];
32187b47708SBarry Smith     else {
32287b47708SBarry Smith       red->zerodiag = PETSC_TRUE;
32387b47708SBarry Smith       red->diag[i]  = 0.0;
32487b47708SBarry Smith     }
32587b47708SBarry Smith   }
3269566063dSJacob Faibussowitsch   PetscCall(VecRestoreArrayRead(diag, &d));
3279566063dSJacob Faibussowitsch   PetscCall(VecDestroy(&diag));
3289566063dSJacob Faibussowitsch   PetscCall(KSPSetUp(red->ksp));
3293ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
330df826632SBarry Smith }
331df826632SBarry Smith 
332d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCApply_Redistribute(PC pc, Vec b, Vec x)
333d71ae5a4SJacob Faibussowitsch {
334df826632SBarry Smith   PC_Redistribute   *red   = (PC_Redistribute *)pc->data;
335181dd334SBarry Smith   PetscInt           dcnt  = red->dcnt, i;
336181dd334SBarry Smith   const PetscInt    *drows = red->drows;
337181dd334SBarry Smith   PetscScalar       *xwork;
338181dd334SBarry Smith   const PetscScalar *bwork, *diag = red->diag;
3393ae97395SBarry Smith   PetscBool          nonzero_guess;
340df826632SBarry Smith 
341df826632SBarry Smith   PetscFunctionBegin;
34248a46eb9SPierre Jolivet   if (!red->work) PetscCall(VecDuplicate(b, &red->work));
3433ae97395SBarry Smith   PetscCall(KSPGetInitialGuessNonzero(red->ksp, &nonzero_guess));
3443ae97395SBarry Smith   if (nonzero_guess) {
3453ae97395SBarry Smith     PetscCall(VecScatterBegin(red->scatter, x, red->x, INSERT_VALUES, SCATTER_FORWARD));
3463ae97395SBarry Smith     PetscCall(VecScatterEnd(red->scatter, x, red->x, INSERT_VALUES, SCATTER_FORWARD));
3473ae97395SBarry Smith   }
3483ae97395SBarry Smith 
349181dd334SBarry Smith   /* compute the rows of solution that have diagonal entries only */
3509566063dSJacob Faibussowitsch   PetscCall(VecSet(x, 0.0)); /* x = diag(A)^{-1} b */
3519566063dSJacob Faibussowitsch   PetscCall(VecGetArray(x, &xwork));
3529566063dSJacob Faibussowitsch   PetscCall(VecGetArrayRead(b, &bwork));
35387b47708SBarry Smith   if (red->zerodiag) {
35487b47708SBarry Smith     for (i = 0; i < dcnt; i++) {
35587b47708SBarry Smith       if (diag[i] == 0.0 && bwork[drows[i]] != 0.0) {
35687b47708SBarry Smith         PetscCheck(!pc->erroriffailure, PETSC_COMM_SELF, PETSC_ERR_CONV_FAILED, "Linear system is inconsistent, zero matrix row but nonzero right hand side");
3579d3446b2SPierre Jolivet         PetscCall(PetscInfo(pc, "Linear system is inconsistent, zero matrix row but nonzero right hand side\n"));
35887b47708SBarry Smith         PetscCall(VecSetInf(x));
35987b47708SBarry Smith         pc->failedreasonrank = PC_INCONSISTENT_RHS;
36087b47708SBarry Smith       }
36187b47708SBarry Smith     }
36287b47708SBarry Smith   }
3632fa5cd67SKarl Rupp   for (i = 0; i < dcnt; i++) xwork[drows[i]] = diag[i] * bwork[drows[i]];
3649566063dSJacob Faibussowitsch   PetscCall(PetscLogFlops(dcnt));
3659566063dSJacob Faibussowitsch   PetscCall(VecRestoreArray(red->work, &xwork));
3669566063dSJacob Faibussowitsch   PetscCall(VecRestoreArrayRead(b, &bwork));
367181dd334SBarry Smith   /* update the right hand side for the reduced system with diagonal rows (and corresponding columns) removed */
3689566063dSJacob Faibussowitsch   PetscCall(MatMult(pc->pmat, x, red->work));
3699566063dSJacob Faibussowitsch   PetscCall(VecAYPX(red->work, -1.0, b)); /* red->work = b - A x */
370181dd334SBarry Smith 
3719566063dSJacob Faibussowitsch   PetscCall(VecScatterBegin(red->scatter, red->work, red->b, INSERT_VALUES, SCATTER_FORWARD));
3729566063dSJacob Faibussowitsch   PetscCall(VecScatterEnd(red->scatter, red->work, red->b, INSERT_VALUES, SCATTER_FORWARD));
3739566063dSJacob Faibussowitsch   PetscCall(KSPSolve(red->ksp, red->b, red->x));
3749566063dSJacob Faibussowitsch   PetscCall(KSPCheckSolve(red->ksp, pc, red->x));
3759566063dSJacob Faibussowitsch   PetscCall(VecScatterBegin(red->scatter, red->x, x, INSERT_VALUES, SCATTER_REVERSE));
3769566063dSJacob Faibussowitsch   PetscCall(VecScatterEnd(red->scatter, red->x, x, INSERT_VALUES, SCATTER_REVERSE));
3773ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
378df826632SBarry Smith }
379df826632SBarry Smith 
380d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCDestroy_Redistribute(PC pc)
381d71ae5a4SJacob Faibussowitsch {
382df826632SBarry Smith   PC_Redistribute  *red  = (PC_Redistribute *)pc->data;
38369ff637eSBarry Smith   PC_FieldSplitLink next = red->splitlinks;
384df826632SBarry Smith 
385df826632SBarry Smith   PetscFunctionBegin;
38669ff637eSBarry Smith   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetIS_C", NULL));
38769ff637eSBarry Smith 
38869ff637eSBarry Smith   while (next) {
38969ff637eSBarry Smith     PC_FieldSplitLink ilink;
39069ff637eSBarry Smith     PetscCall(PetscFree(next->splitname));
39169ff637eSBarry Smith     PetscCall(ISDestroy(&next->is));
39269ff637eSBarry Smith     ilink = next;
39369ff637eSBarry Smith     next  = next->next;
39469ff637eSBarry Smith     PetscCall(PetscFree(ilink));
39569ff637eSBarry Smith   }
3969566063dSJacob Faibussowitsch   PetscCall(VecScatterDestroy(&red->scatter));
3979566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&red->is));
3989566063dSJacob Faibussowitsch   PetscCall(VecDestroy(&red->b));
3999566063dSJacob Faibussowitsch   PetscCall(VecDestroy(&red->x));
4009566063dSJacob Faibussowitsch   PetscCall(KSPDestroy(&red->ksp));
4019566063dSJacob Faibussowitsch   PetscCall(VecDestroy(&red->work));
4029566063dSJacob Faibussowitsch   PetscCall(PetscFree(red->drows));
4039566063dSJacob Faibussowitsch   PetscCall(PetscFree(red->diag));
4049566063dSJacob Faibussowitsch   PetscCall(PetscFree(pc->data));
4053ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
406df826632SBarry Smith }
407df826632SBarry Smith 
408d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCSetFromOptions_Redistribute(PC pc, PetscOptionItems *PetscOptionsObject)
409d71ae5a4SJacob Faibussowitsch {
410df826632SBarry Smith   PC_Redistribute *red = (PC_Redistribute *)pc->data;
411df826632SBarry Smith 
412df826632SBarry Smith   PetscFunctionBegin;
4139566063dSJacob Faibussowitsch   PetscCall(KSPSetFromOptions(red->ksp));
4143ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
415df826632SBarry Smith }
416df826632SBarry Smith 
4175e7ef714SBarry Smith /*@
418f1580f4eSBarry Smith   PCRedistributeGetKSP - Gets the `KSP` created by the `PCREDISTRIBUTE`
4195e7ef714SBarry Smith 
4205e7ef714SBarry Smith   Not Collective
4215e7ef714SBarry Smith 
4225e7ef714SBarry Smith   Input Parameter:
4235e7ef714SBarry Smith . pc - the preconditioner context
4245e7ef714SBarry Smith 
4255e7ef714SBarry Smith   Output Parameter:
426f1580f4eSBarry Smith . innerksp - the inner `KSP`
4275e7ef714SBarry Smith 
4285e7ef714SBarry Smith   Level: advanced
4295e7ef714SBarry Smith 
430*562efe2eSBarry Smith .seealso: [](ch_ksp), `KSP`, `PCREDISTRIBUTE`
4315e7ef714SBarry Smith @*/
432d71ae5a4SJacob Faibussowitsch PetscErrorCode PCRedistributeGetKSP(PC pc, KSP *innerksp)
433d71ae5a4SJacob Faibussowitsch {
4345e7ef714SBarry Smith   PC_Redistribute *red = (PC_Redistribute *)pc->data;
4355e7ef714SBarry Smith 
4365e7ef714SBarry Smith   PetscFunctionBegin;
4375e7ef714SBarry Smith   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
4384f572ea9SToby Isaac   PetscAssertPointer(innerksp, 2);
4395e7ef714SBarry Smith   *innerksp = red->ksp;
4403ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
4415e7ef714SBarry Smith }
4425e7ef714SBarry Smith 
443df826632SBarry Smith /*MC
4447caa67d9SBarry Smith      PCREDISTRIBUTE - Redistributes a matrix for load balancing, removing the rows (and the corresponding columns) that only have a diagonal entry and then
445f1580f4eSBarry Smith      applies a `KSP` to that new smaller matrix
446df826632SBarry Smith 
4477df6f684SBarry Smith      Level: intermediate
4487df6f684SBarry Smith 
44995452b02SPatrick Sanan      Notes:
450f1580f4eSBarry Smith      Options for the redistribute `KSP` and `PC` with the options database prefix -redistribute_
451f1580f4eSBarry Smith 
4527df6f684SBarry Smith      Usually run this with `-ksp_type preonly`
453181dd334SBarry Smith 
4547df6f684SBarry 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
4557df6f684SBarry Smith      -pc_type redistribute -redistribute_ksp_type cg -redistribute_pc_type bjacobi -redistribute_sub_pc_type icc` to take advantage of the symmetry.
456181dd334SBarry Smith 
457baca6076SPierre Jolivet      Supports the function `PCFieldSplitSetIS()`; pass the appropriate reduced field indices to an inner `PCFIELDSPLIT`, set with, for example
45869ff637eSBarry Smith      `-ksp_type preonly -pc_type redistribute -redistribute_pc_type fieldsplit. Does not support the `PCFIELDSPLIT` options database keys.
45969ff637eSBarry Smith 
4607df6f684SBarry 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
4617df6f684SBarry Smith      between MPI processes inside the preconditioner to balance the number of rows on each process.
4622dfef595SBarry Smith 
463f1580f4eSBarry Smith      Developer Note:
46495452b02SPatrick Sanan      Should add an option to this preconditioner to use a partitioner to redistribute the rows to lower communication.
4652dfef595SBarry Smith 
466*562efe2eSBarry Smith .seealso: [](ch_ksp), `PCCreate()`, `PCSetType()`, `PCType`, `PCRedistributeGetKSP()`, `MatZeroRows()`, `PCFieldSplitSetIS()`, `PCFIELDSPLIT`
467df826632SBarry Smith M*/
468df826632SBarry Smith 
469d71ae5a4SJacob Faibussowitsch PETSC_EXTERN PetscErrorCode PCCreate_Redistribute(PC pc)
470d71ae5a4SJacob Faibussowitsch {
471df826632SBarry Smith   PC_Redistribute *red;
472911f9fe8SBarry Smith   const char      *prefix;
473df826632SBarry Smith 
474df826632SBarry Smith   PetscFunctionBegin;
4754dfa11a4SJacob Faibussowitsch   PetscCall(PetscNew(&red));
476df826632SBarry Smith   pc->data = (void *)red;
477df826632SBarry Smith 
478df826632SBarry Smith   pc->ops->apply          = PCApply_Redistribute;
4790a545947SLisandro Dalcin   pc->ops->applytranspose = NULL;
480df826632SBarry Smith   pc->ops->setup          = PCSetUp_Redistribute;
481df826632SBarry Smith   pc->ops->destroy        = PCDestroy_Redistribute;
482df826632SBarry Smith   pc->ops->setfromoptions = PCSetFromOptions_Redistribute;
483df826632SBarry Smith   pc->ops->view           = PCView_Redistribute;
484911f9fe8SBarry Smith 
4859566063dSJacob Faibussowitsch   PetscCall(KSPCreate(PetscObjectComm((PetscObject)pc), &red->ksp));
4863821be0aSBarry Smith   PetscCall(KSPSetNestLevel(red->ksp, pc->kspnestlevel));
4879566063dSJacob Faibussowitsch   PetscCall(KSPSetErrorIfNotConverged(red->ksp, pc->erroriffailure));
4889566063dSJacob Faibussowitsch   PetscCall(PetscObjectIncrementTabLevel((PetscObject)red->ksp, (PetscObject)pc, 1));
4899566063dSJacob Faibussowitsch   PetscCall(PCGetOptionsPrefix(pc, &prefix));
4909566063dSJacob Faibussowitsch   PetscCall(KSPSetOptionsPrefix(red->ksp, prefix));
4919566063dSJacob Faibussowitsch   PetscCall(KSPAppendOptionsPrefix(red->ksp, "redistribute_"));
49269ff637eSBarry Smith   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetIS_C", PCFieldSplitSetIS_Redistribute));
4933ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
494df826632SBarry Smith }
495