xref: /petsc/src/ksp/pc/impls/bddc/bddcgraph.c (revision 14893cbeccd8b5f1bec5fc433e5baa316bdaa2c5)
1 #include <petsc/private/petscimpl.h>
2 #include <../src/ksp/pc/impls/bddc/bddcprivate.h>
3 #include <../src/ksp/pc/impls/bddc/bddcstructs.h>
4 
5 PetscErrorCode PCBDDCGraphGetDirichletDofsB(PCBDDCGraph graph, IS* dirdofs)
6 {
7   PetscErrorCode ierr;
8 
9   PetscFunctionBegin;
10   if (graph->dirdofsB) {
11     ierr = PetscObjectReference((PetscObject)graph->dirdofsB);CHKERRQ(ierr);
12   } else if (graph->has_dirichlet) {
13     PetscInt i,size;
14     PetscInt *dirdofs_idxs;
15 
16     size = 0;
17     for (i=0;i<graph->nvtxs;i++) {
18       if (graph->count[i] && graph->special_dof[i] == PCBDDCGRAPH_DIRICHLET_MARK) size++;
19     }
20 
21     ierr = PetscMalloc1(size,&dirdofs_idxs);CHKERRQ(ierr);
22     size = 0;
23     for (i=0;i<graph->nvtxs;i++) {
24       if (graph->count[i] && graph->special_dof[i] == PCBDDCGRAPH_DIRICHLET_MARK) dirdofs_idxs[size++] = i;
25     }
26     ierr = ISCreateGeneral(PETSC_COMM_SELF,size,dirdofs_idxs,PETSC_OWN_POINTER,&graph->dirdofsB);CHKERRQ(ierr);
27     ierr = PetscObjectReference((PetscObject)graph->dirdofsB);CHKERRQ(ierr);
28   }
29   *dirdofs = graph->dirdofsB;
30   PetscFunctionReturn(0);
31 }
32 
33 PetscErrorCode PCBDDCGraphGetDirichletDofs(PCBDDCGraph graph, IS* dirdofs)
34 {
35   PetscErrorCode ierr;
36 
37   PetscFunctionBegin;
38   if (graph->dirdofs) {
39     ierr = PetscObjectReference((PetscObject)graph->dirdofs);CHKERRQ(ierr);
40   } else if (graph->has_dirichlet) {
41     PetscInt i,size;
42     PetscInt *dirdofs_idxs;
43 
44     size = 0;
45     for (i=0;i<graph->nvtxs;i++) {
46       if (graph->special_dof[i] == PCBDDCGRAPH_DIRICHLET_MARK) size++;
47     }
48 
49     ierr = PetscMalloc1(size,&dirdofs_idxs);CHKERRQ(ierr);
50     size = 0;
51     for (i=0;i<graph->nvtxs;i++) {
52       if (graph->special_dof[i] == PCBDDCGRAPH_DIRICHLET_MARK) dirdofs_idxs[size++] = i;
53     }
54     ierr = ISCreateGeneral(PetscObjectComm((PetscObject)graph->l2gmap),size,dirdofs_idxs,PETSC_OWN_POINTER,&graph->dirdofs);CHKERRQ(ierr);
55     ierr = PetscObjectReference((PetscObject)graph->dirdofs);CHKERRQ(ierr);
56   }
57   *dirdofs = graph->dirdofs;
58   PetscFunctionReturn(0);
59 }
60 
61 PetscErrorCode PCBDDCGraphASCIIView(PCBDDCGraph graph, PetscInt verbosity_level, PetscViewer viewer)
62 {
63   PetscInt       i,j,tabs;
64   PetscInt*      queue_in_global_numbering;
65   PetscErrorCode ierr;
66 
67   PetscFunctionBegin;
68   ierr = PetscViewerASCIIPushSynchronized(viewer);CHKERRQ(ierr);
69   ierr = PetscViewerASCIIGetTab(viewer,&tabs);CHKERRQ(ierr);
70   ierr = PetscViewerASCIIPrintf(viewer,"--------------------------------------------------\n");CHKERRQ(ierr);
71   ierr = PetscViewerFlush(viewer);CHKERRQ(ierr);
72   ierr = PetscViewerASCIISynchronizedPrintf(viewer,"Local BDDC graph for subdomain %04d\n",PetscGlobalRank);CHKERRQ(ierr);
73   ierr = PetscViewerASCIISynchronizedPrintf(viewer,"Number of vertices %d\n",graph->nvtxs);CHKERRQ(ierr);
74   ierr = PetscViewerASCIISynchronizedPrintf(viewer,"Custom minimal size %d\n",graph->custom_minimal_size);CHKERRQ(ierr);
75   if (graph->maxcount != PETSC_MAX_INT) {
76     ierr = PetscViewerASCIISynchronizedPrintf(viewer,"Max count %d\n",graph->maxcount);CHKERRQ(ierr);
77   }
78   ierr = PetscViewerASCIISynchronizedPrintf(viewer,"Topological two dim? %d (set %d)\n",graph->twodim,graph->twodimset);CHKERRQ(ierr);
79   if (verbosity_level > 2) {
80     for (i=0;i<graph->nvtxs;i++) {
81       ierr = PetscViewerASCIISynchronizedPrintf(viewer,"%d:\n",i);CHKERRQ(ierr);
82       ierr = PetscViewerASCIISynchronizedPrintf(viewer,"   which_dof: %d\n",graph->which_dof[i]);CHKERRQ(ierr);
83       ierr = PetscViewerASCIISynchronizedPrintf(viewer,"   special_dof: %d\n",graph->special_dof[i]);CHKERRQ(ierr);
84       ierr = PetscViewerASCIISynchronizedPrintf(viewer,"   neighbours: %d\n",graph->count[i]);CHKERRQ(ierr);
85       ierr = PetscViewerASCIIUseTabs(viewer,PETSC_FALSE);CHKERRQ(ierr);
86       if (graph->count[i]) {
87         ierr = PetscViewerASCIISynchronizedPrintf(viewer,"     set of neighbours:");CHKERRQ(ierr);
88         for (j=0;j<graph->count[i];j++) {
89           ierr = PetscViewerASCIISynchronizedPrintf(viewer," %d",graph->neighbours_set[i][j]);CHKERRQ(ierr);
90         }
91         ierr = PetscViewerASCIISynchronizedPrintf(viewer,"\n");CHKERRQ(ierr);
92       }
93       ierr = PetscViewerASCIISetTab(viewer,tabs);CHKERRQ(ierr);
94       ierr = PetscViewerASCIIUseTabs(viewer,PETSC_TRUE);CHKERRQ(ierr);
95       if (graph->mirrors) {
96         ierr = PetscViewerASCIISynchronizedPrintf(viewer,"   mirrors: %d\n",graph->mirrors[i]);CHKERRQ(ierr);
97         if (graph->mirrors[i]) {
98           ierr = PetscViewerASCIIUseTabs(viewer,PETSC_FALSE);CHKERRQ(ierr);
99           ierr = PetscViewerASCIISynchronizedPrintf(viewer,"     set of mirrors:");CHKERRQ(ierr);
100           for (j=0;j<graph->mirrors[i];j++) {
101             ierr = PetscViewerASCIISynchronizedPrintf(viewer," %d",graph->mirrors_set[i][j]);CHKERRQ(ierr);
102           }
103           ierr = PetscViewerASCIISynchronizedPrintf(viewer,"\n");CHKERRQ(ierr);
104           ierr = PetscViewerASCIISetTab(viewer,tabs);CHKERRQ(ierr);
105           ierr = PetscViewerASCIIUseTabs(viewer,PETSC_TRUE);CHKERRQ(ierr);
106         }
107       }
108       if (verbosity_level > 3) {
109         if (graph->xadj) {
110           ierr = PetscViewerASCIISynchronizedPrintf(viewer,"   local adj list:");CHKERRQ(ierr);
111           ierr = PetscViewerASCIIUseTabs(viewer,PETSC_FALSE);CHKERRQ(ierr);
112           for (j=graph->xadj[i];j<graph->xadj[i+1];j++) {
113             ierr = PetscViewerASCIISynchronizedPrintf(viewer," %d",graph->adjncy[j]);CHKERRQ(ierr);
114           }
115           ierr = PetscViewerASCIISynchronizedPrintf(viewer,"\n");CHKERRQ(ierr);
116           ierr = PetscViewerASCIISetTab(viewer,tabs);CHKERRQ(ierr);
117           ierr = PetscViewerASCIIUseTabs(viewer,PETSC_TRUE);CHKERRQ(ierr);
118         } else {
119           ierr = PetscViewerASCIISynchronizedPrintf(viewer,"   no adj info\n");CHKERRQ(ierr);
120         }
121       }
122       if (graph->n_local_subs) {
123         ierr = PetscViewerASCIISynchronizedPrintf(viewer,"   local sub id: %d\n",graph->local_subs[i]);CHKERRQ(ierr);
124       }
125       ierr = PetscViewerASCIISynchronizedPrintf(viewer,"   interface subset id: %d\n",graph->subset[i]);CHKERRQ(ierr);
126       if (graph->subset[i] && graph->subset_ncc) {
127         ierr = PetscViewerASCIISynchronizedPrintf(viewer,"   ncc for subset: %d\n",graph->subset_ncc[graph->subset[i]-1]);CHKERRQ(ierr);
128       }
129     }
130   }
131   ierr = PetscViewerASCIISynchronizedPrintf(viewer,"Total number of connected components %d\n",graph->ncc);CHKERRQ(ierr);
132   ierr = PetscMalloc1(graph->cptr[graph->ncc],&queue_in_global_numbering);CHKERRQ(ierr);
133   ierr = ISLocalToGlobalMappingApply(graph->l2gmap,graph->cptr[graph->ncc],graph->queue,queue_in_global_numbering);CHKERRQ(ierr);
134   for (i=0;i<graph->ncc;i++) {
135     PetscInt node_num=graph->queue[graph->cptr[i]];
136     PetscBool printcc = PETSC_FALSE;
137     ierr = PetscViewerASCIISynchronizedPrintf(viewer,"  cc %d (size %d, fid %d, neighs:",i,graph->cptr[i+1]-graph->cptr[i],graph->which_dof[node_num]);CHKERRQ(ierr);
138     ierr = PetscViewerASCIIUseTabs(viewer,PETSC_FALSE);CHKERRQ(ierr);
139     for (j=0;j<graph->count[node_num];j++) {
140       ierr = PetscViewerASCIISynchronizedPrintf(viewer," %d",graph->neighbours_set[node_num][j]);CHKERRQ(ierr);
141     }
142     if (verbosity_level > 1) {
143       ierr = PetscViewerASCIISynchronizedPrintf(viewer,"):");CHKERRQ(ierr);
144       if (verbosity_level > 2 || graph->twodim || graph->count[node_num] > 1 || (graph->count[node_num] == 1 && graph->special_dof[node_num] == PCBDDCGRAPH_NEUMANN_MARK)) {
145         printcc = PETSC_TRUE;
146       }
147       if (printcc) {
148         for (j=graph->cptr[i];j<graph->cptr[i+1];j++) {
149           ierr = PetscViewerASCIISynchronizedPrintf(viewer," %d (%d)",graph->queue[j],queue_in_global_numbering[j]);CHKERRQ(ierr);
150         }
151       }
152     } else {
153       ierr = PetscViewerASCIISynchronizedPrintf(viewer,")");CHKERRQ(ierr);
154     }
155     ierr = PetscViewerASCIISynchronizedPrintf(viewer,"\n");CHKERRQ(ierr);
156     ierr = PetscViewerASCIISetTab(viewer,tabs);CHKERRQ(ierr);
157     ierr = PetscViewerASCIIUseTabs(viewer,PETSC_TRUE);CHKERRQ(ierr);
158   }
159   ierr = PetscFree(queue_in_global_numbering);CHKERRQ(ierr);
160   ierr = PetscViewerFlush(viewer);CHKERRQ(ierr);
161   PetscFunctionReturn(0);
162 }
163 
164 PetscErrorCode PCBDDCGraphRestoreCandidatesIS(PCBDDCGraph graph, PetscInt *n_faces, IS *FacesIS[], PetscInt *n_edges, IS *EdgesIS[], IS *VerticesIS)
165 {
166   PetscInt       i;
167   PetscErrorCode ierr;
168 
169   PetscFunctionBegin;
170   if (n_faces) {
171     if (FacesIS) {
172       for (i=0;i<*n_faces;i++) {
173         ierr = ISDestroy(&((*FacesIS)[i]));CHKERRQ(ierr);
174       }
175       ierr = PetscFree(*FacesIS);CHKERRQ(ierr);
176     }
177     *n_faces = 0;
178   }
179   if (n_edges) {
180     if (EdgesIS) {
181       for (i=0;i<*n_edges;i++) {
182         ierr = ISDestroy(&((*EdgesIS)[i]));CHKERRQ(ierr);
183       }
184       ierr = PetscFree(*EdgesIS);CHKERRQ(ierr);
185     }
186     *n_edges = 0;
187   }
188   if (VerticesIS) {
189     ierr = ISDestroy(VerticesIS);CHKERRQ(ierr);
190   }
191   PetscFunctionReturn(0);
192 }
193 
194 PetscErrorCode PCBDDCGraphGetCandidatesIS(PCBDDCGraph graph, PetscInt *n_faces, IS *FacesIS[], PetscInt *n_edges, IS *EdgesIS[], IS *VerticesIS)
195 {
196   IS             *ISForFaces,*ISForEdges,ISForVertices;
197   PetscInt       i,nfc,nec,nvc,*idx,*mark;
198   PetscErrorCode ierr;
199 
200   PetscFunctionBegin;
201   ierr = PetscCalloc1(graph->ncc,&mark);CHKERRQ(ierr);
202   /* loop on ccs to evalute number of faces, edges and vertices */
203   nfc = 0;
204   nec = 0;
205   nvc = 0;
206   for (i=0;i<graph->ncc;i++) {
207     PetscInt repdof = graph->queue[graph->cptr[i]];
208     if (graph->cptr[i+1]-graph->cptr[i] > graph->custom_minimal_size && graph->count[repdof] < graph->maxcount) {
209       if (!graph->twodim && graph->count[repdof] == 1 && graph->special_dof[repdof] != PCBDDCGRAPH_NEUMANN_MARK) {
210         nfc++;
211         mark[i] = 2;
212       } else {
213         nec++;
214         mark[i] = 1;
215       }
216     } else {
217       nvc += graph->cptr[i+1]-graph->cptr[i];
218     }
219   }
220 
221   /* allocate IS arrays for faces, edges. Vertices need a single index set. */
222   if (FacesIS) {
223     ierr = PetscMalloc1(nfc,&ISForFaces);CHKERRQ(ierr);
224   }
225   if (EdgesIS) {
226     ierr = PetscMalloc1(nec,&ISForEdges);CHKERRQ(ierr);
227   }
228   if (VerticesIS) {
229     ierr = PetscMalloc1(nvc,&idx);CHKERRQ(ierr);
230   }
231 
232   /* loop on ccs to compute index sets for faces and edges */
233   if (!graph->queue_sorted) {
234     PetscInt *queue_global;
235 
236     ierr = PetscMalloc1(graph->cptr[graph->ncc],&queue_global);CHKERRQ(ierr);
237     ierr = ISLocalToGlobalMappingApply(graph->l2gmap,graph->cptr[graph->ncc],graph->queue,queue_global);CHKERRQ(ierr);
238     for (i=0;i<graph->ncc;i++) {
239       ierr = PetscSortIntWithArray(graph->cptr[i+1]-graph->cptr[i],&queue_global[graph->cptr[i]],&graph->queue[graph->cptr[i]]);CHKERRQ(ierr);
240     }
241     ierr = PetscFree(queue_global);CHKERRQ(ierr);
242     graph->queue_sorted = PETSC_TRUE;
243   }
244   nfc = 0;
245   nec = 0;
246   for (i=0;i<graph->ncc;i++) {
247     if (mark[i] == 2) {
248       if (FacesIS) {
249         ierr = ISCreateGeneral(PETSC_COMM_SELF,graph->cptr[i+1]-graph->cptr[i],&graph->queue[graph->cptr[i]],PETSC_USE_POINTER,&ISForFaces[nfc]);CHKERRQ(ierr);
250       }
251       nfc++;
252     } else if (mark[i] == 1) {
253       if (EdgesIS) {
254         ierr = ISCreateGeneral(PETSC_COMM_SELF,graph->cptr[i+1]-graph->cptr[i],&graph->queue[graph->cptr[i]],PETSC_USE_POINTER,&ISForEdges[nec]);CHKERRQ(ierr);
255       }
256       nec++;
257     }
258   }
259 
260   /* index set for vertices */
261   if (VerticesIS) {
262     nvc = 0;
263     for (i=0;i<graph->ncc;i++) {
264       if (!mark[i]) {
265         PetscInt j;
266 
267         for (j=graph->cptr[i];j<graph->cptr[i+1];j++) {
268           idx[nvc]=graph->queue[j];
269           nvc++;
270         }
271       }
272     }
273     /* sort vertex set (by local ordering) */
274     ierr = PetscSortInt(nvc,idx);CHKERRQ(ierr);
275     ierr = ISCreateGeneral(PETSC_COMM_SELF,nvc,idx,PETSC_OWN_POINTER,&ISForVertices);CHKERRQ(ierr);
276   }
277   ierr = PetscFree(mark);CHKERRQ(ierr);
278 
279   /* get back info */
280   if (n_faces)       *n_faces = nfc;
281   if (FacesIS)       *FacesIS = ISForFaces;
282   if (n_edges)       *n_edges = nec;
283   if (EdgesIS)       *EdgesIS = ISForEdges;
284   if (VerticesIS) *VerticesIS = ISForVertices;
285   PetscFunctionReturn(0);
286 }
287 
288 PetscErrorCode PCBDDCGraphComputeConnectedComponents(PCBDDCGraph graph)
289 {
290   PetscBool      adapt_interface_reduced;
291   MPI_Comm       interface_comm;
292   PetscMPIInt    size;
293   PetscInt       i;
294   PetscBT        cornerp;
295   PetscErrorCode ierr;
296 
297   PetscFunctionBegin;
298   /* compute connected components locally */
299   ierr = PetscObjectGetComm((PetscObject)(graph->l2gmap),&interface_comm);CHKERRQ(ierr);
300   ierr = PCBDDCGraphComputeConnectedComponentsLocal(graph);CHKERRQ(ierr);
301 
302   cornerp = NULL;
303   if (graph->active_coords) { /* face based corner selection */
304     PetscReal      *wdist;
305     PetscInt       n_neigh,*neigh,*n_shared,**shared;
306     PetscInt       maxc, ns;
307 
308     ierr = PetscBTCreate(graph->nvtxs,&cornerp);CHKERRQ(ierr);
309     ierr = ISLocalToGlobalMappingGetInfo(graph->l2gmap,&n_neigh,&neigh,&n_shared,&shared);CHKERRQ(ierr);
310     for (ns = 1, maxc = 0; ns < n_neigh; ns++) maxc = PetscMax(maxc,n_shared[ns]);
311     ierr = PetscMalloc1(maxc*graph->cdim,&wdist);CHKERRQ(ierr);
312 
313     for (ns = 1; ns < n_neigh; ns++) { /* first proc is self */
314       PetscReal *anchor,mdist;
315       PetscInt  j,k,d,cdim = graph->cdim;
316       PetscInt  point1,point2,point3;
317 /*
318       PetscBool isface = PETSC_FALSE;
319 
320       for (i=0;i<n_shared[ns];i++) {
321         if (graph->count[shared[ns][i]] == 1) {
322           isface = PETSC_TRUE;
323           break;
324         }
325       }
326       if (!isface) continue;
327 */
328       /* import coordinates on shared interface */
329       for (j=0,k=0;j<n_shared[ns];j++)
330         for (d=0;d<cdim;d++)
331           wdist[k++] = graph->coords[shared[ns][j]*cdim+d];
332 
333       /* the dofs are sorted by global numbering, so each rank start from the same id and will detect the same corners */
334       anchor = wdist;
335 
336       /* find the farthest point from the starting one */
337       mdist = -1.0;
338       for (j=0,point1=0;j<n_shared[ns];j++) {
339         PetscReal dist = 0.0;
340 
341         for (d=0;d<cdim;d++) dist += (wdist[j*cdim+d]-anchor[d])*(wdist[j*cdim+d]-anchor[d]);
342         if (dist > mdist) { mdist = dist; point1 = j; }
343       }
344 
345       /* find the farthest point from point1 */
346       anchor = wdist + point1*cdim;
347       mdist  = -1.0;
348       for (j=0,point2=0;j<n_shared[ns];j++) {
349         PetscReal dist = 0.0;
350 
351         for (d=0;d<cdim;d++) dist += (wdist[j*cdim+d]-anchor[d])*(wdist[j*cdim+d]-anchor[d]);
352         if (dist > mdist) { mdist = dist; point2 = j; }
353       }
354 
355       /* find the third point maximizing the triangle area */
356       point3 = point2;
357       if (cdim > 2) {
358         PetscReal a = 0.0;
359 
360         for (d=0;d<cdim;d++) a += (wdist[point1*cdim+d]-wdist[point2*cdim+d])*(wdist[point1*cdim+d]-wdist[point2*cdim+d]);
361         a = PetscSqrtReal(a);
362         mdist = -1.0;
363         for (j=0,point3=0;j<n_shared[ns];j++) {
364           PetscReal area,b = 0.0, c = 0.0,s;
365 
366           for (d=0;d<cdim;d++) {
367             b += (wdist[point1*cdim+d]-wdist[j*cdim+d])*(wdist[point1*cdim+d]-wdist[j*cdim+d]);
368             c += (wdist[point2*cdim+d]-wdist[j*cdim+d])*(wdist[point2*cdim+d]-wdist[j*cdim+d]);
369           }
370           b = PetscSqrtReal(b);
371           c = PetscSqrtReal(c);
372           s = 0.5*(a+b+c);
373           area = s*(s-a)*(s-b)*(s-c); /* Heron's formula, area squared */
374           if (area > mdist) { mdist = area; point3 = j; }
375         }
376       }
377 
378       /* all dofs having the same coordinates will be primal */
379       for (j=0;j<n_shared[ns];j++) {
380         PetscBool same[3] = {PETSC_TRUE,PETSC_TRUE,PETSC_TRUE};
381 
382         for (d=0;d<cdim;d++) {
383           same[0] = (PetscBool)(same[0] && (PetscAbsReal(wdist[j*cdim + d]-wdist[point1*cdim+d]) < PETSC_SMALL));
384           same[1] = (PetscBool)(same[1] && (PetscAbsReal(wdist[j*cdim + d]-wdist[point2*cdim+d]) < PETSC_SMALL));
385           same[2] = (PetscBool)(same[2] && (PetscAbsReal(wdist[j*cdim + d]-wdist[point3*cdim+d]) < PETSC_SMALL));
386         }
387         if (same[0] || same[1] || same[2]) {
388           ierr = PetscBTSet(cornerp,shared[ns][j]);CHKERRQ(ierr);
389         }
390       }
391     }
392     ierr = PetscFree(wdist);CHKERRQ(ierr);
393     ierr = ISLocalToGlobalMappingRestoreInfo(graph->l2gmap,&n_neigh,&neigh,&n_shared,&shared);CHKERRQ(ierr);
394   }
395 
396   /* check consistency of connected components among neighbouring subdomains -> it adapt them in case it is needed */
397   ierr = MPI_Comm_size(interface_comm,&size);CHKERRQ(ierr);
398   adapt_interface_reduced = PETSC_FALSE;
399   if (size > 1) {
400     PetscInt i;
401     PetscBool adapt_interface = cornerp ? PETSC_TRUE : PETSC_FALSE;
402     for (i=0;i<graph->n_subsets && !adapt_interface;i++) {
403       /* We are not sure that on a given subset of the local interface,
404          with two connected components, the latters be the same among sharing subdomains */
405       if (graph->subset_ncc[i] > 1) adapt_interface = PETSC_TRUE;
406     }
407     ierr = MPIU_Allreduce(&adapt_interface,&adapt_interface_reduced,1,MPIU_BOOL,MPI_LOR,interface_comm);CHKERRQ(ierr);
408   }
409 
410   if (graph->n_subsets && adapt_interface_reduced) {
411     PetscBT     subset_cc_adapt;
412     MPI_Request *send_requests,*recv_requests;
413     PetscInt    *send_buffer,*recv_buffer;
414     PetscInt    sum_requests,start_of_recv,start_of_send;
415     PetscInt    *cum_recv_counts;
416     PetscInt    *labels;
417     PetscInt    ncc,cum_queue,mss,mns,j,k,s;
418     PetscInt    **refine_buffer=NULL,*private_labels = NULL;
419     PetscBool   *subset_has_corn,*recv_buffer_bool,*send_buffer_bool;
420 
421     ierr = PetscCalloc1(graph->n_subsets,&subset_has_corn);CHKERRQ(ierr);
422     if (cornerp) {
423       for (i=0;i<graph->n_subsets;i++) {
424         for (j=0;j<graph->subset_size[i];j++) {
425           if (PetscBTLookup(cornerp,graph->subset_idxs[i][j])) {
426             subset_has_corn[i] = PETSC_TRUE;
427             break;
428           }
429         }
430       }
431     }
432     ierr = PetscMalloc1(graph->nvtxs,&labels);CHKERRQ(ierr);
433     ierr = PetscMemzero(labels,graph->nvtxs*sizeof(*labels));CHKERRQ(ierr);
434     for (i=0,k=0;i<graph->ncc;i++) {
435       PetscInt s = 1;
436       for (j=graph->cptr[i];j<graph->cptr[i+1];j++) {
437         if (cornerp && PetscBTLookup(cornerp,graph->queue[j])) {
438           labels[graph->queue[j]] = k+s;
439           s += 1;
440         } else {
441           labels[graph->queue[j]] = k;
442         }
443       }
444       k += s;
445     }
446 
447     /* allocate some space */
448     ierr = PetscMalloc1(graph->n_subsets+1,&cum_recv_counts);CHKERRQ(ierr);
449     ierr = PetscMemzero(cum_recv_counts,(graph->n_subsets+1)*sizeof(*cum_recv_counts));CHKERRQ(ierr);
450 
451     /* first count how many neighbours per connected component I will receive from */
452     cum_recv_counts[0] = 0;
453     for (i=0;i<graph->n_subsets;i++) cum_recv_counts[i+1] = cum_recv_counts[i]+graph->count[graph->subset_idxs[i][0]];
454     ierr = PetscMalloc1(graph->n_subsets,&send_buffer_bool);CHKERRQ(ierr);
455     ierr = PetscMalloc1(cum_recv_counts[graph->n_subsets],&recv_buffer_bool);CHKERRQ(ierr);
456     ierr = PetscMalloc2(cum_recv_counts[graph->n_subsets],&send_requests,cum_recv_counts[graph->n_subsets],&recv_requests);CHKERRQ(ierr);
457     for (i=0;i<cum_recv_counts[graph->n_subsets];i++) {
458       send_requests[i] = MPI_REQUEST_NULL;
459       recv_requests[i] = MPI_REQUEST_NULL;
460     }
461 
462     /* exchange with my neighbours the number of my connected components on the subset of interface */
463     sum_requests = 0;
464     for (i=0;i<graph->n_subsets;i++) {
465       send_buffer_bool[i] = (PetscBool)(graph->subset_ncc[i] > 1 || subset_has_corn[i]);
466     }
467     for (i=0;i<graph->n_subsets;i++) {
468       PetscMPIInt neigh,tag;
469       PetscInt    count,*neighs;
470 
471       count  = graph->count[graph->subset_idxs[i][0]];
472       neighs = graph->neighbours_set[graph->subset_idxs[i][0]];
473       ierr   = PetscMPIIntCast(2*graph->subset_ref_node[i],&tag);CHKERRQ(ierr);
474       for (k=0;k<count;k++) {
475 
476         ierr = PetscMPIIntCast(neighs[k],&neigh);CHKERRQ(ierr);
477         ierr = MPI_Isend(send_buffer_bool + i,           1,MPIU_BOOL,neigh,tag,interface_comm,&send_requests[sum_requests]);CHKERRQ(ierr);
478         ierr = MPI_Irecv(recv_buffer_bool + sum_requests,1,MPIU_BOOL,neigh,tag,interface_comm,&recv_requests[sum_requests]);CHKERRQ(ierr);
479         sum_requests++;
480       }
481     }
482     ierr = MPI_Waitall(sum_requests,recv_requests,MPI_STATUSES_IGNORE);CHKERRQ(ierr);
483     ierr = MPI_Waitall(sum_requests,send_requests,MPI_STATUSES_IGNORE);CHKERRQ(ierr);
484 
485     /* determine the subsets I have to adapt (those having more than 1 cc) */
486     ierr = PetscBTCreate(graph->n_subsets,&subset_cc_adapt);CHKERRQ(ierr);
487     ierr = PetscBTMemzero(graph->n_subsets,subset_cc_adapt);CHKERRQ(ierr);
488     for (i=0;i<graph->n_subsets;i++) {
489       if (graph->subset_ncc[i] > 1 || subset_has_corn[i]) {
490         ierr = PetscBTSet(subset_cc_adapt,i);CHKERRQ(ierr);
491         continue;
492       }
493       for (j=cum_recv_counts[i];j<cum_recv_counts[i+1];j++){
494          if (recv_buffer_bool[j]) {
495           ierr = PetscBTSet(subset_cc_adapt,i);CHKERRQ(ierr);
496           break;
497         }
498       }
499     }
500     ierr = PetscFree(send_buffer_bool);CHKERRQ(ierr);
501     ierr = PetscFree(recv_buffer_bool);CHKERRQ(ierr);
502     ierr = PetscFree(subset_has_corn);CHKERRQ(ierr);
503 
504     /* determine send/recv buffers sizes */
505     j = 0;
506     mss = 0;
507     for (i=0;i<graph->n_subsets;i++) {
508       if (PetscBTLookup(subset_cc_adapt,i)) {
509         j  += graph->subset_size[i];
510         mss = PetscMax(graph->subset_size[i],mss);
511       }
512     }
513     k = 0;
514     mns = 0;
515     for (i=0;i<graph->n_subsets;i++) {
516       if (PetscBTLookup(subset_cc_adapt,i)) {
517         k  += (cum_recv_counts[i+1]-cum_recv_counts[i])*graph->subset_size[i];
518         mns = PetscMax(cum_recv_counts[i+1]-cum_recv_counts[i],mns);
519       }
520     }
521     ierr = PetscMalloc2(j,&send_buffer,k,&recv_buffer);CHKERRQ(ierr);
522 
523     /* fill send buffer (order matters: subset_idxs ordered by global ordering) */
524     j = 0;
525     for (i=0;i<graph->n_subsets;i++)
526       if (PetscBTLookup(subset_cc_adapt,i))
527         for (k=0;k<graph->subset_size[i];k++)
528           send_buffer[j++] = labels[graph->subset_idxs[i][k]];
529 
530     /* now exchange the data */
531     start_of_recv = 0;
532     start_of_send = 0;
533     sum_requests  = 0;
534     for (i=0;i<graph->n_subsets;i++) {
535       if (PetscBTLookup(subset_cc_adapt,i)) {
536         PetscMPIInt neigh,tag;
537         PetscInt    size_of_send = graph->subset_size[i];
538 
539         j    = graph->subset_idxs[i][0];
540         ierr = PetscMPIIntCast(2*graph->subset_ref_node[i]+1,&tag);CHKERRQ(ierr);
541         for (k=0;k<graph->count[j];k++) {
542           ierr = PetscMPIIntCast(graph->neighbours_set[j][k],&neigh);CHKERRQ(ierr);
543           ierr = MPI_Isend(&send_buffer[start_of_send],size_of_send,MPIU_INT,neigh,tag,interface_comm,&send_requests[sum_requests]);CHKERRQ(ierr);
544           ierr = MPI_Irecv(&recv_buffer[start_of_recv],size_of_send,MPIU_INT,neigh,tag,interface_comm,&recv_requests[sum_requests]);CHKERRQ(ierr);
545           start_of_recv += size_of_send;
546           sum_requests++;
547         }
548         start_of_send += size_of_send;
549       }
550     }
551     ierr = MPI_Waitall(sum_requests,recv_requests,MPI_STATUSES_IGNORE);CHKERRQ(ierr);
552 
553     /* refine connected components */
554     start_of_recv = 0;
555     /* allocate some temporary space */
556     if (mss) {
557       ierr = PetscMalloc1(mss,&refine_buffer);CHKERRQ(ierr);
558       ierr = PetscMalloc2(mss*(mns+1),&refine_buffer[0],mss,&private_labels);CHKERRQ(ierr);
559     }
560     ncc = 0;
561     cum_queue = 0;
562     graph->cptr[0] = 0;
563     for (i=0;i<graph->n_subsets;i++) {
564       if (PetscBTLookup(subset_cc_adapt,i)) {
565         PetscInt subset_counter = 0;
566         PetscInt sharingprocs = cum_recv_counts[i+1]-cum_recv_counts[i]+1; /* count myself */
567         PetscInt buffer_size = graph->subset_size[i];
568 
569         /* compute pointers */
570         for (j=1;j<buffer_size;j++) refine_buffer[j] = refine_buffer[j-1] + sharingprocs;
571         /* analyze contributions from subdomains that share the i-th subset
572            The structure of refine_buffer is suitable to find intersections of ccs among sharingprocs.
573            supposing the current subset is shared by 3 processes and has dimension 5 with global dofs 0,1,2,3,4 (local 0,4,3,1,2)
574            sharing procs connected components:
575              neigh 0: [0 1 4], [2 3], labels [4,7]  (2 connected components)
576              neigh 1: [0 1], [2 3 4], labels [3 2]  (2 connected components)
577              neigh 2: [0 4], [1], [2 3], labels [1 5 6] (3 connected components)
578            refine_buffer will be filled as:
579              [ 4, 3, 1;
580                4, 2, 1;
581                7, 2, 6;
582                4, 3, 5;
583                7, 2, 6; ];
584            The connected components in local ordering are [0], [1], [2 3], [4] */
585         /* fill temp_buffer */
586         for (k=0;k<buffer_size;k++) refine_buffer[k][0] = labels[graph->subset_idxs[i][k]];
587         for (j=0;j<sharingprocs-1;j++) {
588           for (k=0;k<buffer_size;k++) refine_buffer[k][j+1] = recv_buffer[start_of_recv+k];
589           start_of_recv += buffer_size;
590         }
591         ierr = PetscMemzero(private_labels,buffer_size*sizeof(PetscInt));CHKERRQ(ierr);
592         for (j=0;j<buffer_size;j++) {
593           if (!private_labels[j]) { /* found a new cc  */
594             PetscBool same_set;
595 
596             graph->cptr[ncc] = cum_queue;
597             ncc++;
598             subset_counter++;
599             private_labels[j] = subset_counter;
600             graph->queue[cum_queue++] = graph->subset_idxs[i][j];
601             for (k=j+1;k<buffer_size;k++) { /* check for other nodes in new cc */
602               same_set = PETSC_TRUE;
603               for (s=0;s<sharingprocs;s++) {
604                 if (refine_buffer[j][s] != refine_buffer[k][s]) {
605                   same_set = PETSC_FALSE;
606                   break;
607                 }
608               }
609               if (same_set) {
610                 private_labels[k] = subset_counter;
611                 graph->queue[cum_queue++] = graph->subset_idxs[i][k];
612               }
613             }
614           }
615         }
616         graph->cptr[ncc]     = cum_queue;
617         graph->subset_ncc[i] = subset_counter;
618         graph->queue_sorted  = PETSC_FALSE;
619       } else { /* this subset does not need to be adapted */
620         ierr = PetscMemcpy(graph->queue+cum_queue,graph->subset_idxs[i],graph->subset_size[i]*sizeof(PetscInt));CHKERRQ(ierr);
621         ncc++;
622         cum_queue += graph->subset_size[i];
623         graph->cptr[ncc] = cum_queue;
624       }
625     }
626     graph->cptr[ncc] = cum_queue;
627     graph->ncc       = ncc;
628     if (mss) {
629       ierr = PetscFree2(refine_buffer[0],private_labels);CHKERRQ(ierr);
630       ierr = PetscFree(refine_buffer);CHKERRQ(ierr);
631     }
632     ierr = PetscFree(labels);CHKERRQ(ierr);
633     ierr = MPI_Waitall(sum_requests,send_requests,MPI_STATUSES_IGNORE);CHKERRQ(ierr);
634     ierr = PetscFree2(send_requests,recv_requests);CHKERRQ(ierr);
635     ierr = PetscFree2(send_buffer,recv_buffer);CHKERRQ(ierr);
636     ierr = PetscFree(cum_recv_counts);CHKERRQ(ierr);
637     ierr = PetscBTDestroy(&subset_cc_adapt);CHKERRQ(ierr);
638   }
639   ierr = PetscBTDestroy(&cornerp);CHKERRQ(ierr);
640 
641   /* Determine if we are in 2D or 3D */
642   if (!graph->twodimset) {
643     PetscBool twodim = PETSC_TRUE;
644     for (i=0;i<graph->ncc;i++) {
645       PetscInt repdof = graph->queue[graph->cptr[i]];
646       PetscInt ccsize = graph->cptr[i+1]-graph->cptr[i];
647       if (graph->count[repdof] > 1 && ccsize > graph->custom_minimal_size) {
648         twodim = PETSC_FALSE;
649         break;
650       }
651     }
652     ierr = MPIU_Allreduce(&twodim,&graph->twodim,1,MPIU_BOOL,MPI_LAND,PetscObjectComm((PetscObject)graph->l2gmap));CHKERRQ(ierr);
653     graph->twodimset = PETSC_TRUE;
654   }
655   PetscFunctionReturn(0);
656 }
657 
658 
659 PETSC_STATIC_INLINE PetscErrorCode PCBDDCGraphComputeCC_Private(PCBDDCGraph graph,PetscInt pid,PetscInt* queue_tip,PetscInt n_prev,PetscInt* n_added)
660 {
661   PetscInt       i,j,n;
662   PetscInt       *xadj = graph->xadj,*adjncy = graph->adjncy;
663   PetscBT        touched = graph->touched;
664   PetscBool      havecsr = (PetscBool)(!!xadj);
665   PetscBool      havesubs = (PetscBool)(!!graph->n_local_subs);
666   PetscErrorCode ierr;
667 
668   PetscFunctionBegin;
669   n = 0;
670   if (havecsr && !havesubs) {
671     for (i=-n_prev;i<0;i++) {
672       PetscInt start_dof = queue_tip[i];
673       /* we assume that if a dof has a size 1 adjacency list and the corresponding entry is negative, it is connected to all dofs */
674       if (xadj[start_dof+1]-xadj[start_dof] == 1 && adjncy[xadj[start_dof]] < 0) {
675         for (j=0;j<graph->subset_size[pid-1];j++) { /* pid \in [1,graph->n_subsets] */
676           PetscInt dof = graph->subset_idxs[pid-1][j];
677           if (!PetscBTLookup(touched,dof) && graph->subset[dof] == pid) {
678             ierr = PetscBTSet(touched,dof);CHKERRQ(ierr);
679             queue_tip[n] = dof;
680             n++;
681           }
682         }
683       } else {
684         for (j=xadj[start_dof];j<xadj[start_dof+1];j++) {
685           PetscInt dof = adjncy[j];
686           if (!PetscBTLookup(touched,dof) && graph->subset[dof] == pid) {
687             ierr = PetscBTSet(touched,dof);CHKERRQ(ierr);
688             queue_tip[n] = dof;
689             n++;
690           }
691         }
692       }
693     }
694   } else if (havecsr && havesubs) {
695     PetscInt sid = graph->local_subs[queue_tip[-n_prev]];
696     for (i=-n_prev;i<0;i++) {
697       PetscInt start_dof = queue_tip[i];
698       /* we assume that if a dof has a size 1 adjacency list and the corresponding entry is negative, it is connected to all dofs belonging to the local sub */
699       if (xadj[start_dof+1]-xadj[start_dof] == 1 && adjncy[xadj[start_dof]] < 0) {
700         for (j=0;j<graph->subset_size[pid-1];j++) { /* pid \in [1,graph->n_subsets] */
701           PetscInt dof = graph->subset_idxs[pid-1][j];
702           if (!PetscBTLookup(touched,dof) && graph->subset[dof] == pid && graph->local_subs[dof] == sid) {
703             ierr = PetscBTSet(touched,dof);CHKERRQ(ierr);
704             queue_tip[n] = dof;
705             n++;
706           }
707         }
708       } else {
709         for (j=xadj[start_dof];j<xadj[start_dof+1];j++) {
710           PetscInt dof = adjncy[j];
711           if (!PetscBTLookup(touched,dof) && graph->subset[dof] == pid && graph->local_subs[dof] == sid) {
712             ierr = PetscBTSet(touched,dof);CHKERRQ(ierr);
713             queue_tip[n] = dof;
714             n++;
715           }
716         }
717       }
718     }
719   } else if (havesubs) { /* sub info only */
720     PetscInt sid = graph->local_subs[queue_tip[-n_prev]];
721     for (j=0;j<graph->subset_size[pid-1];j++) { /* pid \in [1,graph->n_subsets] */
722       PetscInt dof = graph->subset_idxs[pid-1][j];
723       if (!PetscBTLookup(touched,dof) && graph->subset[dof] == pid && graph->local_subs[dof] == sid) {
724         ierr = PetscBTSet(touched,dof);CHKERRQ(ierr);
725         queue_tip[n] = dof;
726         n++;
727       }
728     }
729   } else {
730     for (j=0;j<graph->subset_size[pid-1];j++) { /* pid \in [1,graph->n_subsets] */
731       PetscInt dof = graph->subset_idxs[pid-1][j];
732       if (!PetscBTLookup(touched,dof) && graph->subset[dof] == pid) {
733         ierr = PetscBTSet(touched,dof);CHKERRQ(ierr);
734         queue_tip[n] = dof;
735         n++;
736       }
737     }
738   }
739   *n_added = n;
740   PetscFunctionReturn(0);
741 }
742 
743 PetscErrorCode PCBDDCGraphComputeConnectedComponentsLocal(PCBDDCGraph graph)
744 {
745   PetscInt       ncc,cum_queue,n;
746   PetscMPIInt    commsize;
747   PetscErrorCode ierr;
748 
749   PetscFunctionBegin;
750   if (!graph->setupcalled) SETERRQ(PetscObjectComm((PetscObject)graph->l2gmap),PETSC_ERR_ORDER,"PCBDDCGraphSetUp should be called first");
751   /* quiet return if there isn't any local info */
752   if (!graph->xadj && !graph->n_local_subs) {
753     PetscFunctionReturn(0);
754   }
755 
756   /* reset any previous search of connected components */
757   ierr = PetscBTMemzero(graph->nvtxs,graph->touched);CHKERRQ(ierr);
758   ierr = MPI_Comm_size(PetscObjectComm((PetscObject)graph->l2gmap),&commsize);CHKERRQ(ierr);
759   if (commsize > graph->commsizelimit) {
760     PetscInt i;
761     for (i=0;i<graph->nvtxs;i++) {
762       if (graph->special_dof[i] == PCBDDCGRAPH_DIRICHLET_MARK || !graph->count[i]) {
763         ierr = PetscBTSet(graph->touched,i);CHKERRQ(ierr);
764       }
765     }
766   }
767 
768   /* begin search for connected components */
769   cum_queue = 0;
770   ncc = 0;
771   for (n=0;n<graph->n_subsets;n++) {
772     PetscInt pid = n+1;  /* partition labeled by 0 is discarded */
773     PetscInt found = 0,prev = 0,first = 0,ncc_pid = 0;
774     while (found != graph->subset_size[n]) {
775       PetscInt added = 0;
776       if (!prev) { /* search for new starting dof */
777         while (PetscBTLookup(graph->touched,graph->subset_idxs[n][first])) first++;
778         ierr = PetscBTSet(graph->touched,graph->subset_idxs[n][first]);CHKERRQ(ierr);
779         graph->queue[cum_queue] = graph->subset_idxs[n][first];
780         graph->cptr[ncc] = cum_queue;
781         prev = 1;
782         cum_queue++;
783         found++;
784         ncc_pid++;
785         ncc++;
786       }
787       ierr = PCBDDCGraphComputeCC_Private(graph,pid,graph->queue + cum_queue,prev,&added);CHKERRQ(ierr);
788       if (!added) {
789         graph->subset_ncc[n] = ncc_pid;
790         graph->cptr[ncc] = cum_queue;
791       }
792       prev = added;
793       found += added;
794       cum_queue += added;
795       if (added && found == graph->subset_size[n]) {
796         graph->subset_ncc[n] = ncc_pid;
797         graph->cptr[ncc] = cum_queue;
798       }
799     }
800   }
801   graph->ncc = ncc;
802   graph->queue_sorted = PETSC_FALSE;
803   PetscFunctionReturn(0);
804 }
805 
806 PetscErrorCode PCBDDCGraphSetUp(PCBDDCGraph graph, PetscInt custom_minimal_size, IS neumann_is, IS dirichlet_is, PetscInt n_ISForDofs, IS ISForDofs[], IS custom_primal_vertices)
807 {
808   IS             subset,subset_n;
809   MPI_Comm       comm;
810   const PetscInt *is_indices;
811   PetscInt       n_neigh,*neigh,*n_shared,**shared,*queue_global;
812   PetscInt       i,j,k,s,total_counts,nodes_touched,is_size;
813   PetscMPIInt    commsize;
814   PetscBool      same_set,mirrors_found;
815   PetscErrorCode ierr;
816 
817   PetscFunctionBegin;
818   PetscValidLogicalCollectiveInt(graph->l2gmap,custom_minimal_size,2);
819   if (neumann_is) {
820     PetscValidHeaderSpecific(neumann_is,IS_CLASSID,3);
821     PetscCheckSameComm(graph->l2gmap,1,neumann_is,3);
822   }
823   graph->has_dirichlet = PETSC_FALSE;
824   if (dirichlet_is) {
825     PetscValidHeaderSpecific(dirichlet_is,IS_CLASSID,4);
826     PetscCheckSameComm(graph->l2gmap,1,dirichlet_is,4);
827     graph->has_dirichlet = PETSC_TRUE;
828   }
829   PetscValidLogicalCollectiveInt(graph->l2gmap,n_ISForDofs,5);
830   for (i=0;i<n_ISForDofs;i++) {
831     PetscValidHeaderSpecific(ISForDofs[i],IS_CLASSID,6);
832     PetscCheckSameComm(graph->l2gmap,1,ISForDofs[i],6);
833   }
834   if (custom_primal_vertices) {
835     PetscValidHeaderSpecific(custom_primal_vertices,IS_CLASSID,7);
836     PetscCheckSameComm(graph->l2gmap,1,custom_primal_vertices,7);
837   }
838   ierr = PetscObjectGetComm((PetscObject)(graph->l2gmap),&comm);CHKERRQ(ierr);
839   ierr = MPI_Comm_size(comm,&commsize);CHKERRQ(ierr);
840 
841   /* custom_minimal_size */
842   graph->custom_minimal_size = custom_minimal_size;
843   /* get info l2gmap and allocate work vectors  */
844   ierr = ISLocalToGlobalMappingGetInfo(graph->l2gmap,&n_neigh,&neigh,&n_shared,&shared);CHKERRQ(ierr);
845   /* check if we have any local periodic nodes (periodic BCs) */
846   mirrors_found = PETSC_FALSE;
847   if (graph->nvtxs && n_neigh) {
848     for (i=0; i<n_shared[0]; i++) graph->count[shared[0][i]] += 1;
849     for (i=0; i<n_shared[0]; i++) {
850       if (graph->count[shared[0][i]] > 1) {
851         mirrors_found = PETSC_TRUE;
852         break;
853       }
854     }
855   }
856   /* compute local mirrors (if any) */
857   if (mirrors_found) {
858     IS       to,from;
859     PetscInt *local_indices,*global_indices;
860 
861     ierr = ISCreateStride(PETSC_COMM_SELF,graph->nvtxs,0,1,&to);CHKERRQ(ierr);
862     ierr = ISLocalToGlobalMappingApplyIS(graph->l2gmap,to,&from);CHKERRQ(ierr);
863     /* get arrays of local and global indices */
864     ierr = PetscMalloc1(graph->nvtxs,&local_indices);CHKERRQ(ierr);
865     ierr = ISGetIndices(to,(const PetscInt**)&is_indices);CHKERRQ(ierr);
866     ierr = PetscMemcpy(local_indices,is_indices,graph->nvtxs*sizeof(PetscInt));CHKERRQ(ierr);
867     ierr = ISRestoreIndices(to,(const PetscInt**)&is_indices);CHKERRQ(ierr);
868     ierr = PetscMalloc1(graph->nvtxs,&global_indices);CHKERRQ(ierr);
869     ierr = ISGetIndices(from,(const PetscInt**)&is_indices);CHKERRQ(ierr);
870     ierr = PetscMemcpy(global_indices,is_indices,graph->nvtxs*sizeof(PetscInt));CHKERRQ(ierr);
871     ierr = ISRestoreIndices(from,(const PetscInt**)&is_indices);CHKERRQ(ierr);
872     /* allocate space for mirrors */
873     ierr = PetscMalloc2(graph->nvtxs,&graph->mirrors,graph->nvtxs,&graph->mirrors_set);CHKERRQ(ierr);
874     ierr = PetscMemzero(graph->mirrors,graph->nvtxs*sizeof(PetscInt));CHKERRQ(ierr);
875     graph->mirrors_set[0] = 0;
876 
877     k=0;
878     for (i=0;i<n_shared[0];i++) {
879       j=shared[0][i];
880       if (graph->count[j] > 1) {
881         graph->mirrors[j]++;
882         k++;
883       }
884     }
885     /* allocate space for set of mirrors */
886     ierr = PetscMalloc1(k,&graph->mirrors_set[0]);CHKERRQ(ierr);
887     for (i=1;i<graph->nvtxs;i++)
888       graph->mirrors_set[i]=graph->mirrors_set[i-1]+graph->mirrors[i-1];
889 
890     /* fill arrays */
891     ierr = PetscMemzero(graph->mirrors,graph->nvtxs*sizeof(PetscInt));CHKERRQ(ierr);
892     for (j=0;j<n_shared[0];j++) {
893       i=shared[0][j];
894       if (graph->count[i] > 1)
895         graph->mirrors_set[i][graph->mirrors[i]++]=global_indices[i];
896     }
897     ierr = PetscSortIntWithArray(graph->nvtxs,global_indices,local_indices);CHKERRQ(ierr);
898     for (i=0;i<graph->nvtxs;i++) {
899       if (graph->mirrors[i] > 0) {
900         ierr = PetscFindInt(graph->mirrors_set[i][0],graph->nvtxs,global_indices,&k);CHKERRQ(ierr);
901         j = global_indices[k];
902         while ( k > 0 && global_indices[k-1] == j) k--;
903         for (j=0;j<graph->mirrors[i];j++) {
904           graph->mirrors_set[i][j]=local_indices[k+j];
905         }
906         ierr = PetscSortInt(graph->mirrors[i],graph->mirrors_set[i]);CHKERRQ(ierr);
907       }
908     }
909     ierr = PetscFree(local_indices);CHKERRQ(ierr);
910     ierr = PetscFree(global_indices);CHKERRQ(ierr);
911     ierr = ISDestroy(&to);CHKERRQ(ierr);
912     ierr = ISDestroy(&from);CHKERRQ(ierr);
913   }
914   ierr = PetscMemzero(graph->count,graph->nvtxs*sizeof(*graph->count));CHKERRQ(ierr);
915 
916   /* Count total number of neigh per node */
917   k = 0;
918   for (i=1;i<n_neigh;i++) {
919     k += n_shared[i];
920     for (j=0;j<n_shared[i];j++) {
921       graph->count[shared[i][j]] += 1;
922     }
923   }
924   /* Allocate space for storing the set of neighbours for each node */
925   if (graph->nvtxs) {
926     ierr = PetscMalloc1(k,&graph->neighbours_set[0]);CHKERRQ(ierr);
927   }
928   for (i=1;i<graph->nvtxs;i++) { /* dont count myself */
929     graph->neighbours_set[i]=graph->neighbours_set[i-1]+graph->count[i-1];
930   }
931   /* Get information for sharing subdomains */
932   ierr = PetscMemzero(graph->count,graph->nvtxs*sizeof(*graph->count));CHKERRQ(ierr);
933   for (i=1;i<n_neigh;i++) { /* dont count myself */
934     s = n_shared[i];
935     for (j=0;j<s;j++) {
936       k = shared[i][j];
937       graph->neighbours_set[k][graph->count[k]] = neigh[i];
938       graph->count[k] += 1;
939     }
940   }
941   /* sort set of sharing subdomains */
942   for (i=0;i<graph->nvtxs;i++) {
943     ierr = PetscSortRemoveDupsInt(&graph->count[i],graph->neighbours_set[i]);CHKERRQ(ierr);
944   }
945   /* free memory allocated by ISLocalToGlobalMappingGetInfo */
946   ierr = ISLocalToGlobalMappingRestoreInfo(graph->l2gmap,&n_neigh,&neigh,&n_shared,&shared);CHKERRQ(ierr);
947 
948   /*
949      Get info for dofs splitting
950      User can specify just a subset; an additional field is considered as a complementary field
951   */
952   for (i=0,k=0;i<n_ISForDofs;i++) {
953     PetscInt bs;
954 
955     ierr = ISGetBlockSize(ISForDofs[i],&bs);CHKERRQ(ierr);
956     k   += bs;
957   }
958   for (i=0;i<graph->nvtxs;i++) graph->which_dof[i] = k; /* by default a dof belongs to the complement set */
959   for (i=0,k=0;i<n_ISForDofs;i++) {
960     PetscInt bs;
961 
962     ierr = ISGetLocalSize(ISForDofs[i],&is_size);CHKERRQ(ierr);
963     ierr = ISGetBlockSize(ISForDofs[i],&bs);CHKERRQ(ierr);
964     ierr = ISGetIndices(ISForDofs[i],(const PetscInt**)&is_indices);CHKERRQ(ierr);
965     for (j=0;j<is_size/bs;j++) {
966       PetscInt b;
967 
968       for (b=0;b<bs;b++) {
969         PetscInt jj = bs*j + b;
970 
971         if (is_indices[jj] > -1 && is_indices[jj] < graph->nvtxs) { /* out of bounds indices (if any) are skipped */
972           graph->which_dof[is_indices[jj]] = k+b;
973         }
974       }
975     }
976     ierr = ISRestoreIndices(ISForDofs[i],(const PetscInt**)&is_indices);CHKERRQ(ierr);
977     k   += bs;
978   }
979 
980   /* Take into account Neumann nodes */
981   if (neumann_is) {
982     ierr = ISGetLocalSize(neumann_is,&is_size);CHKERRQ(ierr);
983     ierr = ISGetIndices(neumann_is,(const PetscInt**)&is_indices);CHKERRQ(ierr);
984     for (i=0;i<is_size;i++) {
985       if (is_indices[i] > -1 && is_indices[i] < graph->nvtxs) { /* out of bounds indices (if any) are skipped */
986         graph->special_dof[is_indices[i]] = PCBDDCGRAPH_NEUMANN_MARK;
987       }
988     }
989     ierr = ISRestoreIndices(neumann_is,(const PetscInt**)&is_indices);CHKERRQ(ierr);
990   }
991   /* Take into account Dirichlet nodes (they overwrite any neumann boundary mark previously set) */
992   if (dirichlet_is) {
993     ierr = ISGetLocalSize(dirichlet_is,&is_size);CHKERRQ(ierr);
994     ierr = ISGetIndices(dirichlet_is,(const PetscInt**)&is_indices);CHKERRQ(ierr);
995     for (i=0;i<is_size;i++){
996       if (is_indices[i] > -1 && is_indices[i] < graph->nvtxs) { /* out of bounds indices (if any) are skipped */
997         if (commsize > graph->commsizelimit) { /* dirichlet nodes treated as internal */
998           ierr = PetscBTSet(graph->touched,is_indices[i]);CHKERRQ(ierr);
999           graph->subset[is_indices[i]] = 0;
1000         }
1001         graph->special_dof[is_indices[i]] = PCBDDCGRAPH_DIRICHLET_MARK;
1002       }
1003     }
1004     ierr = ISRestoreIndices(dirichlet_is,(const PetscInt**)&is_indices);CHKERRQ(ierr);
1005   }
1006   /* mark local periodic nodes (if any) and adapt CSR graph (if any) */
1007   if (graph->mirrors) {
1008     for (i=0;i<graph->nvtxs;i++)
1009       if (graph->mirrors[i])
1010         graph->special_dof[i] = PCBDDCGRAPH_LOCAL_PERIODIC_MARK;
1011 
1012     if (graph->xadj) {
1013       PetscInt *new_xadj,*new_adjncy;
1014       /* sort CSR graph */
1015       for (i=0;i<graph->nvtxs;i++)
1016         ierr = PetscSortInt(graph->xadj[i+1]-graph->xadj[i],&graph->adjncy[graph->xadj[i]]);CHKERRQ(ierr);
1017 
1018       /* adapt local CSR graph in case of local periodicity */
1019       k = 0;
1020       for (i=0;i<graph->nvtxs;i++)
1021         for (j=graph->xadj[i];j<graph->xadj[i+1];j++)
1022           k += graph->mirrors[graph->adjncy[j]];
1023 
1024       ierr = PetscMalloc1(graph->nvtxs+1,&new_xadj);CHKERRQ(ierr);
1025       ierr = PetscMalloc1(k+graph->xadj[graph->nvtxs],&new_adjncy);CHKERRQ(ierr);
1026       new_xadj[0] = 0;
1027       for (i=0;i<graph->nvtxs;i++) {
1028         k = graph->xadj[i+1]-graph->xadj[i];
1029         ierr = PetscMemcpy(&new_adjncy[new_xadj[i]],&graph->adjncy[graph->xadj[i]],k*sizeof(PetscInt));CHKERRQ(ierr);
1030         new_xadj[i+1] = new_xadj[i]+k;
1031         for (j=graph->xadj[i];j<graph->xadj[i+1];j++) {
1032           k = graph->mirrors[graph->adjncy[j]];
1033           ierr = PetscMemcpy(&new_adjncy[new_xadj[i+1]],graph->mirrors_set[graph->adjncy[j]],k*sizeof(PetscInt));CHKERRQ(ierr);
1034           new_xadj[i+1] += k;
1035         }
1036         k = new_xadj[i+1]-new_xadj[i];
1037         ierr = PetscSortRemoveDupsInt(&k,&new_adjncy[new_xadj[i]]);CHKERRQ(ierr);
1038         new_xadj[i+1] = new_xadj[i]+k;
1039       }
1040       /* set new CSR into graph */
1041       ierr = PetscFree(graph->xadj);CHKERRQ(ierr);
1042       ierr = PetscFree(graph->adjncy);CHKERRQ(ierr);
1043       graph->xadj = new_xadj;
1044       graph->adjncy = new_adjncy;
1045     }
1046   }
1047 
1048   /* mark special nodes (if any) -> each will become a single node equivalence class */
1049   if (custom_primal_vertices) {
1050     ierr = ISGetLocalSize(custom_primal_vertices,&is_size);CHKERRQ(ierr);
1051     ierr = ISGetIndices(custom_primal_vertices,(const PetscInt**)&is_indices);CHKERRQ(ierr);
1052     for (i=0,j=0;i<is_size;i++){
1053       if (is_indices[i] > -1 && is_indices[i] < graph->nvtxs  && graph->special_dof[is_indices[i]] != PCBDDCGRAPH_DIRICHLET_MARK) { /* out of bounds indices (if any) are skipped */
1054         graph->special_dof[is_indices[i]] = PCBDDCGRAPH_SPECIAL_MARK-j;
1055         j++;
1056       }
1057     }
1058     ierr = ISRestoreIndices(custom_primal_vertices,(const PetscInt**)&is_indices);CHKERRQ(ierr);
1059   }
1060 
1061   /* mark interior nodes (if commsize > graph->commsizelimit) as touched and belonging to partition number 0 */
1062   if (commsize > graph->commsizelimit) {
1063     for (i=0;i<graph->nvtxs;i++) {
1064       if (!graph->count[i]) {
1065         ierr = PetscBTSet(graph->touched,i);CHKERRQ(ierr);
1066         graph->subset[i] = 0;
1067       }
1068     }
1069   }
1070 
1071   /* init graph structure and compute default subsets */
1072   nodes_touched = 0;
1073   for (i=0;i<graph->nvtxs;i++) {
1074     if (PetscBTLookup(graph->touched,i)) {
1075       nodes_touched++;
1076     }
1077   }
1078   i = 0;
1079   graph->ncc = 0;
1080   total_counts = 0;
1081 
1082   /* allocated space for queues */
1083   if (commsize == graph->commsizelimit) {
1084     ierr = PetscMalloc2(graph->nvtxs+1,&graph->cptr,graph->nvtxs,&graph->queue);CHKERRQ(ierr);
1085   } else {
1086     PetscInt nused = graph->nvtxs - nodes_touched;
1087     ierr = PetscMalloc2(nused+1,&graph->cptr,nused,&graph->queue);CHKERRQ(ierr);
1088   }
1089 
1090   while (nodes_touched<graph->nvtxs) {
1091     /*  find first untouched node in local ordering */
1092     while (PetscBTLookup(graph->touched,i)) i++;
1093     ierr = PetscBTSet(graph->touched,i);CHKERRQ(ierr);
1094     graph->subset[i] = graph->ncc+1;
1095     graph->cptr[graph->ncc] = total_counts;
1096     graph->queue[total_counts] = i;
1097     total_counts++;
1098     nodes_touched++;
1099     /* now find all other nodes having the same set of sharing subdomains */
1100     for (j=i+1;j<graph->nvtxs;j++) {
1101       /* check for same number of sharing subdomains, dof number and same special mark */
1102       if (!PetscBTLookup(graph->touched,j) && graph->count[i] == graph->count[j] && graph->which_dof[i] == graph->which_dof[j] && graph->special_dof[i] == graph->special_dof[j]) {
1103         /* check for same set of sharing subdomains */
1104         same_set = PETSC_TRUE;
1105         for (k=0;k<graph->count[j];k++){
1106           if (graph->neighbours_set[i][k] != graph->neighbours_set[j][k]) {
1107             same_set = PETSC_FALSE;
1108           }
1109         }
1110         /* I have found a friend of mine */
1111         if (same_set) {
1112           ierr = PetscBTSet(graph->touched,j);CHKERRQ(ierr);
1113           graph->subset[j] = graph->ncc+1;
1114           nodes_touched++;
1115           graph->queue[total_counts] = j;
1116           total_counts++;
1117         }
1118       }
1119     }
1120     graph->ncc++;
1121   }
1122   /* set default number of subsets (at this point no info on csr and/or local_subs has been taken into account, so n_subsets = ncc */
1123   graph->n_subsets = graph->ncc;
1124   ierr = PetscMalloc1(graph->n_subsets,&graph->subset_ncc);CHKERRQ(ierr);
1125   for (i=0;i<graph->n_subsets;i++) {
1126     graph->subset_ncc[i] = 1;
1127   }
1128   /* final pointer */
1129   graph->cptr[graph->ncc] = total_counts;
1130 
1131   /* For consistency reasons (among neighbours), I need to sort (by global ordering) each connected component */
1132   /* Get a reference node (min index in global ordering) for each subset for tagging messages */
1133   ierr = PetscMalloc1(graph->ncc,&graph->subset_ref_node);CHKERRQ(ierr);
1134   ierr = PetscMalloc1(graph->cptr[graph->ncc],&queue_global);CHKERRQ(ierr);
1135   ierr = ISLocalToGlobalMappingApply(graph->l2gmap,graph->cptr[graph->ncc],graph->queue,queue_global);CHKERRQ(ierr);
1136   for (j=0;j<graph->ncc;j++) {
1137     ierr = PetscSortIntWithArray(graph->cptr[j+1]-graph->cptr[j],&queue_global[graph->cptr[j]],&graph->queue[graph->cptr[j]]);CHKERRQ(ierr);
1138     graph->subset_ref_node[j] = graph->queue[graph->cptr[j]];
1139   }
1140   ierr = PetscFree(queue_global);CHKERRQ(ierr);
1141   graph->queue_sorted = PETSC_TRUE;
1142 
1143   /* save information on subsets (needed when analyzing the connected components) */
1144   if (graph->ncc) {
1145     ierr = PetscMalloc2(graph->ncc,&graph->subset_size,graph->ncc,&graph->subset_idxs);CHKERRQ(ierr);
1146     ierr = PetscMalloc1(graph->cptr[graph->ncc],&graph->subset_idxs[0]);CHKERRQ(ierr);
1147     ierr = PetscMemzero(graph->subset_idxs[0],graph->cptr[graph->ncc]*sizeof(PetscInt));CHKERRQ(ierr);
1148     for (j=1;j<graph->ncc;j++) {
1149       graph->subset_size[j-1] = graph->cptr[j] - graph->cptr[j-1];
1150       graph->subset_idxs[j] = graph->subset_idxs[j-1] + graph->subset_size[j-1];
1151     }
1152     graph->subset_size[graph->ncc-1] = graph->cptr[graph->ncc] - graph->cptr[graph->ncc-1];
1153     ierr = PetscMemcpy(graph->subset_idxs[0],graph->queue,graph->cptr[graph->ncc]*sizeof(PetscInt));CHKERRQ(ierr);
1154   }
1155 
1156   /* renumber reference nodes */
1157   ierr = ISCreateGeneral(PetscObjectComm((PetscObject)(graph->l2gmap)),graph->ncc,graph->subset_ref_node,PETSC_COPY_VALUES,&subset_n);CHKERRQ(ierr);
1158   ierr = ISLocalToGlobalMappingApplyIS(graph->l2gmap,subset_n,&subset);CHKERRQ(ierr);
1159   ierr = ISDestroy(&subset_n);CHKERRQ(ierr);
1160   ierr = ISRenumber(subset,NULL,NULL,&subset_n);CHKERRQ(ierr);
1161   ierr = ISDestroy(&subset);CHKERRQ(ierr);
1162   ierr = ISGetLocalSize(subset_n,&k);CHKERRQ(ierr);
1163   if (k != graph->ncc) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Invalid size of new subset! %D != %D",k,graph->ncc);
1164   ierr = ISGetIndices(subset_n,&is_indices);CHKERRQ(ierr);
1165   ierr = PetscMemcpy(graph->subset_ref_node,is_indices,graph->ncc*sizeof(PetscInt));CHKERRQ(ierr);
1166   ierr = ISRestoreIndices(subset_n,&is_indices);CHKERRQ(ierr);
1167   ierr = ISDestroy(&subset_n);CHKERRQ(ierr);
1168 
1169   /* free workspace */
1170   graph->setupcalled = PETSC_TRUE;
1171   PetscFunctionReturn(0);
1172 }
1173 
1174 PetscErrorCode PCBDDCGraphResetCoords(PCBDDCGraph graph)
1175 {
1176   PetscErrorCode ierr;
1177 
1178   PetscFunctionBegin;
1179   if (!graph) PetscFunctionReturn(0);
1180   ierr = PetscFree(graph->coords);CHKERRQ(ierr);
1181   graph->cdim  = 0;
1182   graph->cnloc = 0;
1183   graph->cloc  = PETSC_FALSE;
1184   PetscFunctionReturn(0);
1185 }
1186 
1187 PetscErrorCode PCBDDCGraphResetCSR(PCBDDCGraph graph)
1188 {
1189   PetscErrorCode ierr;
1190 
1191   PetscFunctionBegin;
1192   if (!graph) PetscFunctionReturn(0);
1193   if (graph->freecsr) {
1194     ierr = PetscFree(graph->xadj);CHKERRQ(ierr);
1195     ierr = PetscFree(graph->adjncy);CHKERRQ(ierr);
1196   } else {
1197     graph->xadj = NULL;
1198     graph->adjncy = NULL;
1199   }
1200   graph->freecsr = PETSC_FALSE;
1201   graph->nvtxs_csr = 0;
1202   PetscFunctionReturn(0);
1203 }
1204 
1205 PetscErrorCode PCBDDCGraphReset(PCBDDCGraph graph)
1206 {
1207   PetscErrorCode ierr;
1208 
1209   PetscFunctionBegin;
1210   if (!graph) PetscFunctionReturn(0);
1211   ierr = ISLocalToGlobalMappingDestroy(&graph->l2gmap);CHKERRQ(ierr);
1212   ierr = PetscFree(graph->subset_ncc);CHKERRQ(ierr);
1213   ierr = PetscFree(graph->subset_ref_node);CHKERRQ(ierr);
1214   if (graph->nvtxs) {
1215     ierr = PetscFree(graph->neighbours_set[0]);CHKERRQ(ierr);
1216   }
1217   ierr = PetscBTDestroy(&graph->touched);CHKERRQ(ierr);
1218   ierr = PetscFree5(graph->count,
1219                     graph->neighbours_set,
1220                     graph->subset,
1221                     graph->which_dof,
1222                     graph->special_dof);CHKERRQ(ierr);
1223   ierr = PetscFree2(graph->cptr,graph->queue);CHKERRQ(ierr);
1224   if (graph->mirrors) {
1225     ierr = PetscFree(graph->mirrors_set[0]);CHKERRQ(ierr);
1226   }
1227   ierr = PetscFree2(graph->mirrors,graph->mirrors_set);CHKERRQ(ierr);
1228   if (graph->subset_idxs) {
1229     ierr = PetscFree(graph->subset_idxs[0]);CHKERRQ(ierr);
1230   }
1231   ierr = PetscFree2(graph->subset_size,graph->subset_idxs);CHKERRQ(ierr);
1232   ierr = ISDestroy(&graph->dirdofs);CHKERRQ(ierr);
1233   ierr = ISDestroy(&graph->dirdofsB);CHKERRQ(ierr);
1234   if (graph->n_local_subs) {
1235     ierr = PetscFree(graph->local_subs);CHKERRQ(ierr);
1236   }
1237   graph->has_dirichlet       = PETSC_FALSE;
1238   graph->twodimset           = PETSC_FALSE;
1239   graph->twodim              = PETSC_FALSE;
1240   graph->nvtxs               = 0;
1241   graph->nvtxs_global        = 0;
1242   graph->n_subsets           = 0;
1243   graph->custom_minimal_size = 1;
1244   graph->n_local_subs        = 0;
1245   graph->maxcount            = PETSC_MAX_INT;
1246   graph->setupcalled         = PETSC_FALSE;
1247   PetscFunctionReturn(0);
1248 }
1249 
1250 PetscErrorCode PCBDDCGraphInit(PCBDDCGraph graph, ISLocalToGlobalMapping l2gmap, PetscInt N, PetscInt maxcount)
1251 {
1252   PetscInt       n;
1253   PetscErrorCode ierr;
1254 
1255   PetscFunctionBegin;
1256   PetscValidPointer(graph,1);
1257   PetscValidHeaderSpecific(l2gmap,IS_LTOGM_CLASSID,2);
1258   PetscValidLogicalCollectiveInt(l2gmap,N,3);
1259   PetscValidLogicalCollectiveInt(l2gmap,maxcount,4);
1260   /* raise an error if already allocated */
1261   if (graph->nvtxs_global) SETERRQ(PetscObjectComm((PetscObject)l2gmap),PETSC_ERR_PLIB,"BDDCGraph already initialized");
1262   /* set number of vertices */
1263   ierr = PetscObjectReference((PetscObject)l2gmap);CHKERRQ(ierr);
1264   graph->l2gmap = l2gmap;
1265   ierr = ISLocalToGlobalMappingGetSize(l2gmap,&n);CHKERRQ(ierr);
1266   graph->nvtxs = n;
1267   graph->nvtxs_global = N;
1268   /* allocate used space */
1269   ierr = PetscBTCreate(graph->nvtxs,&graph->touched);CHKERRQ(ierr);
1270   ierr = PetscMalloc5(graph->nvtxs,&graph->count,
1271                       graph->nvtxs,&graph->neighbours_set,
1272                       graph->nvtxs,&graph->subset,
1273                       graph->nvtxs,&graph->which_dof,
1274                       graph->nvtxs,&graph->special_dof);CHKERRQ(ierr);
1275   /* zeroes memory */
1276   ierr = PetscMemzero(graph->count,graph->nvtxs*sizeof(PetscInt));CHKERRQ(ierr);
1277   ierr = PetscMemzero(graph->subset,graph->nvtxs*sizeof(PetscInt));CHKERRQ(ierr);
1278   /* use -1 as a default value for which_dof array */
1279   for (n=0;n<graph->nvtxs;n++) graph->which_dof[n] = -1;
1280   ierr = PetscMemzero(graph->special_dof,graph->nvtxs*sizeof(PetscInt));CHKERRQ(ierr);
1281   /* zeroes first pointer to neighbour set */
1282   if (graph->nvtxs) {
1283     graph->neighbours_set[0] = 0;
1284   }
1285   /* zeroes workspace for values of ncc */
1286   graph->subset_ncc = 0;
1287   graph->subset_ref_node = 0;
1288   /* maxcount for cc */
1289   graph->maxcount = maxcount;
1290   PetscFunctionReturn(0);
1291 }
1292 
1293 PetscErrorCode PCBDDCGraphDestroy(PCBDDCGraph* graph)
1294 {
1295   PetscErrorCode ierr;
1296 
1297   PetscFunctionBegin;
1298   ierr = PCBDDCGraphResetCSR(*graph);CHKERRQ(ierr);
1299   ierr = PCBDDCGraphResetCoords(*graph);CHKERRQ(ierr);
1300   ierr = PCBDDCGraphReset(*graph);CHKERRQ(ierr);
1301   ierr = PetscFree(*graph);CHKERRQ(ierr);
1302   PetscFunctionReturn(0);
1303 }
1304 
1305 PetscErrorCode PCBDDCGraphCreate(PCBDDCGraph *graph)
1306 {
1307   PCBDDCGraph    new_graph;
1308   PetscErrorCode ierr;
1309 
1310   PetscFunctionBegin;
1311   ierr = PetscNew(&new_graph);CHKERRQ(ierr);
1312   new_graph->custom_minimal_size = 1;
1313   new_graph->commsizelimit = 1;
1314   *graph = new_graph;
1315   PetscFunctionReturn(0);
1316 }
1317