xref: /petsc/src/dm/impls/network/network.c (revision 990be990e8fb7ae2ca4c5a741a757b1e601b5881)
1 #include <petsc/private/dmnetworkimpl.h> /*I  "petscdmnetwork.h"  I*/
2 
3 PetscLogEvent DMNetwork_LayoutSetUp;
4 PetscLogEvent DMNetwork_SetUpNetwork;
5 PetscLogEvent DMNetwork_Distribute;
6 
7 /*
8   Creates the component header and value objects for a network point
9 */
10 static PetscErrorCode SetUpNetworkHeaderComponentValue(DM dm, DMNetworkComponentHeader header, DMNetworkComponentValue cvalue)
11 {
12   PetscFunctionBegin;
13   /* Allocate arrays for component information */
14   PetscCall(PetscCalloc5(header->maxcomps, &header->size, header->maxcomps, &header->key, header->maxcomps, &header->offset, header->maxcomps, &header->nvar, header->maxcomps, &header->offsetvarrel));
15   PetscCall(PetscCalloc1(header->maxcomps, &cvalue->data));
16 
17   /* The size of the header is the size of struct _p_DMNetworkComponentHeader. Since the struct contains PetscInt pointers we cannot use sizeof(struct). So, we need to explicitly calculate the size.
18    If the data header struct changes then this header size calculation needs to be updated. */
19   header->hsize = sizeof(struct _p_DMNetworkComponentHeader) + 5 * header->maxcomps * sizeof(PetscInt);
20   header->hsize /= sizeof(DMNetworkComponentGenericDataType);
21   PetscFunctionReturn(0);
22 }
23 
24 PetscErrorCode DMNetworkInitializeHeaderComponentData(DM dm)
25 {
26   DM_Network *network = (DM_Network *)dm->data;
27   PetscInt    np, p, defaultnumcomp = DMNETWORK_MAX_COMP_AT_POINT_DEFAULT;
28 
29   PetscFunctionBegin;
30   np = network->cloneshared->pEnd - network->cloneshared->pStart;
31   if (network->header)
32     for (p = 0; p < np; p++) {
33       network->header[p].maxcomps = defaultnumcomp;
34       PetscCall(SetUpNetworkHeaderComponentValue(dm, &network->header[p], &network->cvalue[p]));
35     }
36 
37   PetscFunctionReturn(0);
38 }
39 /*@
40   DMNetworkGetPlex - Gets the Plex DM associated with this network DM
41 
42   Not collective
43 
44   Input Parameters:
45 . dm - the dm object
46 
47   Output Parameters:
48 . plexdm - the plex dm object
49 
50   Level: Advanced
51 
52 .seealso: `DMNetworkCreate()`
53 @*/
54 PetscErrorCode DMNetworkGetPlex(DM dm, DM *plexdm)
55 {
56   DM_Network *network = (DM_Network *)dm->data;
57 
58   PetscFunctionBegin;
59   *plexdm = network->plex;
60   PetscFunctionReturn(0);
61 }
62 
63 /*@
64   DMNetworkGetNumSubNetworks - Gets the the number of subnetworks
65 
66   Not collective
67 
68   Input Parameter:
69 . dm - the dm object
70 
71   Output Parameters:
72 + nsubnet - local number of subnetworks
73 - Nsubnet - global number of subnetworks
74 
75   Level: beginner
76 
77 .seealso: `DMNetworkCreate()`, `DMNetworkSetNumSubNetworks()`
78 @*/
79 PetscErrorCode DMNetworkGetNumSubNetworks(DM dm, PetscInt *nsubnet, PetscInt *Nsubnet)
80 {
81   DM_Network *network = (DM_Network *)dm->data;
82 
83   PetscFunctionBegin;
84   if (nsubnet) *nsubnet = network->cloneshared->nsubnet;
85   if (Nsubnet) *Nsubnet = network->cloneshared->Nsubnet;
86   PetscFunctionReturn(0);
87 }
88 
89 /*@
90   DMNetworkSetNumSubNetworks - Sets the number of subnetworks
91 
92   Collective on dm
93 
94   Input Parameters:
95 + dm - the dm object
96 . nsubnet - local number of subnetworks
97 - Nsubnet - global number of subnetworks
98 
99    Level: beginner
100 
101 .seealso: `DMNetworkCreate()`, `DMNetworkGetNumSubNetworks()`
102 @*/
103 PetscErrorCode DMNetworkSetNumSubNetworks(DM dm, PetscInt nsubnet, PetscInt Nsubnet)
104 {
105   DM_Network *network = (DM_Network *)dm->data;
106 
107   PetscFunctionBegin;
108   PetscCheck(network->cloneshared->Nsubnet == 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_INCOMP, "Network sizes already set, cannot resize the network");
109 
110   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
111   PetscValidLogicalCollectiveInt(dm, nsubnet, 2);
112   PetscValidLogicalCollectiveInt(dm, Nsubnet, 3);
113 
114   if (Nsubnet == PETSC_DECIDE) {
115     PetscCheck(nsubnet >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Number of local subnetworks %" PetscInt_FMT " cannot be less than 0", nsubnet);
116     PetscCall(MPIU_Allreduce(&nsubnet, &Nsubnet, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
117   }
118   PetscCheck(Nsubnet >= 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_INCOMP, "Number of global subnetworks %" PetscInt_FMT " cannot be less than 1", Nsubnet);
119 
120   network->cloneshared->Nsubnet = Nsubnet;
121   network->cloneshared->nsubnet = 0; /* initial value; will be determind by DMNetworkAddSubnetwork() */
122   PetscCall(PetscCalloc1(Nsubnet, &network->cloneshared->subnet));
123 
124   /* num of shared vertices */
125   network->cloneshared->nsvtx = 0;
126   network->cloneshared->Nsvtx = 0;
127   PetscFunctionReturn(0);
128 }
129 
130 /*@
131   DMNetworkAddSubnetwork - Add a subnetwork
132 
133   Collective on dm
134 
135   Input Parameters:
136 + dm - the dm object
137 . name - name of the subnetwork
138 . ne - number of local edges of this subnetwork
139 - edgelist - list of edges for this subnetwork, this is a one dimensional array with pairs of entries being the two vertices (in global numbering of the vertices) of each edge,
140 $            [first vertex of first edge, second vertex of first edge, first vertex of second edge, second vertex of second edge, etc]
141 
142   Output Parameters:
143 . netnum - global index of the subnetwork
144 
145   Notes:
146   There is no copy involved in this operation, only the pointer is referenced. The edgelist should
147   not be destroyed before the call to DMNetworkLayoutSetUp()
148 
149   A network can comprise of a single subnetwork OR multiple subnetworks. For a single subnetwork, the subnetwork can be read either in serial or parallel. For a multiple subnetworks,
150   each subnetwork topology needs to be set on a unique rank and the communicator size needs to be at least equal to the number of subnetworks.
151 
152   Level: beginner
153 
154   Example usage:
155   Consider the following networks:
156   1) A single subnetwork:
157 .vb
158  network 0:
159  rank[0]:
160    v0 --> v2; v1 --> v2
161  rank[1]:
162    v3 --> v5; v4 --> v5
163 .ve
164 
165  The resulting input
166  network 0:
167  rank[0]:
168    ne = 2
169    edgelist = [0 2 | 1 2]
170  rank[1]:
171    ne = 2
172    edgelist = [3 5 | 4 5]
173 
174   2) Two subnetworks:
175 .vb
176  subnetwork 0:
177  rank[0]:
178    v0 --> v2; v2 --> v1; v1 --> v3;
179  subnetwork 1:
180  rank[1]:
181    v0 --> v3; v3 --> v2; v2 --> v1;
182 .ve
183 
184  The resulting input
185  subnetwork 0:
186  rank[0]:
187    ne = 3
188    edgelist = [0 2 | 2 1 | 1 3]
189  rank[1]:
190    ne = 0
191    edgelist = NULL
192 
193  subnetwork 1:
194  rank[0]:
195    ne = 0
196    edgelist = NULL
197  rank[1]:
198    edgelist = [0 3 | 3 2 | 2 1]
199 
200 .seealso: `DMNetworkCreate()`, `DMNetworkSetNumSubnetworks()`
201 @*/
202 PetscErrorCode DMNetworkAddSubnetwork(DM dm, const char *name, PetscInt ne, PetscInt edgelist[], PetscInt *netnum)
203 {
204   DM_Network *network = (DM_Network *)dm->data;
205   PetscInt    i, Nedge, j, Nvtx, nvtx, nvtx_min = -1, nvtx_max = 0;
206   PetscBT     table;
207 
208   PetscFunctionBegin;
209   for (i = 0; i < ne; i++) PetscCheck(edgelist[2 * i] != edgelist[2 * i + 1], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " has the same vertex %" PetscInt_FMT " at each endpoint", i, edgelist[2 * i]);
210 
211   i = 0;
212   if (ne) nvtx_min = nvtx_max = edgelist[0];
213   for (j = 0; j < ne; j++) {
214     nvtx_min = PetscMin(nvtx_min, edgelist[i]);
215     nvtx_max = PetscMax(nvtx_max, edgelist[i]);
216     i++;
217     nvtx_min = PetscMin(nvtx_min, edgelist[i]);
218     nvtx_max = PetscMax(nvtx_max, edgelist[i]);
219     i++;
220   }
221   Nvtx = nvtx_max - nvtx_min + 1; /* approximated total local nvtx for this subnet */
222 
223   /* Get exact local nvtx for this subnet: counting local values between nvtx_min and nvtx_max */
224   PetscCall(PetscBTCreate(Nvtx, &table));
225   PetscCall(PetscBTMemzero(Nvtx, table));
226   i = 0;
227   for (j = 0; j < ne; j++) {
228     PetscCall(PetscBTSet(table, edgelist[i++] - nvtx_min));
229     PetscCall(PetscBTSet(table, edgelist[i++] - nvtx_min));
230   }
231   nvtx = 0;
232   for (j = 0; j < Nvtx; j++) {
233     if (PetscBTLookup(table, j)) nvtx++;
234   }
235 
236   /* Get global total Nvtx = max(edgelist[])+1 for this subnet */
237   PetscCall(MPIU_Allreduce(&nvtx_max, &Nvtx, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
238   Nvtx++;
239   PetscCall(PetscBTDestroy(&table));
240 
241   /* Get global total Nedge for this subnet */
242   PetscCall(MPIU_Allreduce(&ne, &Nedge, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
243 
244   i = network->cloneshared->nsubnet;
245   if (name) PetscCall(PetscStrcpy(network->cloneshared->subnet[i].name, name));
246   network->cloneshared->subnet[i].nvtx     = nvtx; /* include ghost vertices */
247   network->cloneshared->subnet[i].nedge    = ne;
248   network->cloneshared->subnet[i].edgelist = edgelist;
249   network->cloneshared->subnet[i].Nvtx     = Nvtx;
250   network->cloneshared->subnet[i].Nedge    = Nedge;
251 
252   /* ----------------------------------------------------------
253    p=v or e;
254    subnet[0].pStart   = 0
255    subnet[i+1].pStart = subnet[i].pEnd = subnet[i].pStart + (nE[i] or NV[i])
256    ----------------------------------------------------------------------- */
257   /* GLOBAL subnet[].vStart and vEnd, used by DMNetworkLayoutSetUp() */
258   network->cloneshared->subnet[i].vStart = network->cloneshared->NVertices;
259   network->cloneshared->subnet[i].vEnd   = network->cloneshared->subnet[i].vStart + network->cloneshared->subnet[i].Nvtx; /* global vEnd of subnet[i] */
260 
261   network->cloneshared->nVertices += nvtx; /* include ghost vertices */
262   network->cloneshared->NVertices += network->cloneshared->subnet[i].Nvtx;
263 
264   /* LOCAL subnet[].eStart and eEnd, used by DMNetworkLayoutSetUp() */
265   network->cloneshared->subnet[i].eStart = network->cloneshared->nEdges;
266   network->cloneshared->subnet[i].eEnd   = network->cloneshared->subnet[i].eStart + ne;
267   network->cloneshared->nEdges += ne;
268   network->cloneshared->NEdges += network->cloneshared->subnet[i].Nedge;
269 
270   PetscCall(PetscStrcpy(network->cloneshared->subnet[i].name, name));
271   if (netnum) *netnum = network->cloneshared->nsubnet;
272   network->cloneshared->nsubnet++;
273   PetscFunctionReturn(0);
274 }
275 
276 /*@C
277   DMNetworkSharedVertexGetInfo - Get info of a shared vertex struct, see petsc/private/dmnetworkimpl.h
278 
279   Not collective
280 
281   Input Parameters:
282 + dm - the DM object
283 - v - vertex point
284 
285   Output Parameters:
286 + gidx - global number of this shared vertex in the internal dmplex
287 . n - number of subnetworks that share this vertex
288 - sv - array of size n: sv[2*i,2*i+1]=(net[i], idx[i]), i=0,...,n-1
289 
290   Level: intermediate
291 
292 .seealso: `DMNetworkGetSharedVertices()`
293 @*/
294 PetscErrorCode DMNetworkSharedVertexGetInfo(DM dm, PetscInt v, PetscInt *gidx, PetscInt *n, const PetscInt **sv)
295 {
296   DM_Network *network = (DM_Network *)dm->data;
297   SVtx       *svtx    = network->cloneshared->svtx;
298   PetscInt    i, gidx_tmp;
299 
300   PetscFunctionBegin;
301   PetscCall(DMNetworkGetGlobalVertexIndex(dm, v, &gidx_tmp));
302   PetscCall(PetscTableFind(network->cloneshared->svtable, gidx_tmp + 1, &i));
303   PetscCheck(i > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "input vertex is not a shared vertex");
304 
305   i--;
306   if (gidx) *gidx = gidx_tmp;
307   if (n) *n = svtx[i].n;
308   if (sv) *sv = svtx[i].sv;
309   PetscFunctionReturn(0);
310 }
311 
312 /*
313   VtxGetInfo - Get info of an input vertex=(net,idx)
314 
315   Input Parameters:
316 + Nsvtx - global num of shared vertices
317 . svtx - array of shared vertices (global)
318 - (net,idx) - subnet number and local index for a vertex
319 
320   Output Parameters:
321 + gidx - global index of (net,idx)
322 . svtype - see petsc/private/dmnetworkimpl.h
323 - svtx_idx - ordering in the svtx array
324 */
325 static inline PetscErrorCode VtxGetInfo(PetscInt Nsvtx, SVtx *svtx, PetscInt net, PetscInt idx, PetscInt *gidx, SVtxType *svtype, PetscInt *svtx_idx)
326 {
327   PetscInt i, j, *svto, g_idx;
328   SVtxType vtype;
329 
330   PetscFunctionBegin;
331   if (!Nsvtx) PetscFunctionReturn(0);
332 
333   g_idx = -1;
334   vtype = SVNONE;
335 
336   for (i = 0; i < Nsvtx; i++) {
337     if (net == svtx[i].sv[0] && idx == svtx[i].sv[1]) {
338       g_idx = svtx[i].gidx;
339       vtype = SVFROM;
340     } else { /* loop over svtx[i].n */
341       for (j = 1; j < svtx[i].n; j++) {
342         svto = svtx[i].sv + 2 * j;
343         if (net == svto[0] && idx == svto[1]) {
344           /* input vertex net.idx is a shared to_vertex, output its global index and its svtype */
345           g_idx = svtx[i].gidx; /* output gidx for to_vertex */
346           vtype = SVTO;
347         }
348       }
349     }
350     if (vtype != SVNONE) break;
351   }
352   if (gidx) *gidx = g_idx;
353   if (svtype) *svtype = vtype;
354   if (svtx_idx) *svtx_idx = i;
355   PetscFunctionReturn(0);
356 }
357 
358 /*
359   TableAddSVtx - Add a new shared vertice from sedgelist[k] to a ctable svta
360 
361   Input:  network, sedgelist, k, svta
362   Output: svta, tdata, ta2sv
363 */
364 static inline PetscErrorCode TableAddSVtx(DM_Network *network, PetscInt *sedgelist, PetscInt k, PetscTable svta, PetscInt *tdata, PetscInt *ta2sv)
365 {
366   PetscInt net, idx, gidx;
367 
368   PetscFunctionBegin;
369   net  = sedgelist[k];
370   idx  = sedgelist[k + 1];
371   gidx = network->cloneshared->subnet[net].vStart + idx;
372   PetscCall(PetscTableAdd(svta, gidx + 1, *tdata + 1, INSERT_VALUES));
373 
374   ta2sv[*tdata] = k; /* maps tdata to index of sedgelist */
375   (*tdata)++;
376   PetscFunctionReturn(0);
377 }
378 
379 /*
380   SharedVtxCreate - Create an array of global shared vertices. See SVtx and SVtxType in dmnetworkimpl.h
381 
382   Input:  dm, Nsedgelist, sedgelist
383 
384   Note: Output svtx is organized as
385         sv(net[0],idx[0]) --> sv(net[1],idx[1])
386                           --> sv(net[1],idx[1])
387                           ...
388                           --> sv(net[n-1],idx[n-1])
389         and net[0] < net[1] < ... < net[n-1]
390         where sv[0] has SVFROM type, sv[i], i>0, has SVTO type.
391  */
392 static PetscErrorCode SharedVtxCreate(DM dm, PetscInt Nsedgelist, PetscInt *sedgelist)
393 {
394   SVtx              *svtx = NULL;
395   PetscInt          *sv, k, j, nsv, *tdata, **ta2sv;
396   PetscTable        *svtas;
397   PetscInt           gidx, net, idx, i, nta, ita, idx_from, idx_to, n, *net_tmp, *idx_tmp, *gidx_tmp;
398   DM_Network        *network = (DM_Network *)dm->data;
399   PetscTablePosition ppos;
400 
401   PetscFunctionBegin;
402   /* (1) Crete an array of ctables svtas to map (net,idx) -> gidx; a svtas[] for a shared/merged vertex */
403   PetscCall(PetscCalloc3(Nsedgelist, &svtas, Nsedgelist, &tdata, 2 * Nsedgelist, &ta2sv));
404 
405   k   = 0; /* sedgelist vertex counter j = 4*k */
406   nta = 0; /* num of svta tables created = num of groups of shared vertices */
407 
408   /* for j=0 */
409   PetscCall(PetscTableCreate(2 * Nsedgelist, network->cloneshared->NVertices + 1, &svtas[nta]));
410   PetscCall(PetscMalloc1(2 * Nsedgelist, &ta2sv[nta]));
411 
412   PetscCall(TableAddSVtx(network, sedgelist, k, svtas[nta], &tdata[nta], ta2sv[nta]));
413   PetscCall(TableAddSVtx(network, sedgelist, k + 2, svtas[nta], &tdata[nta], ta2sv[nta]));
414   nta++;
415   k += 4;
416 
417   for (j = 1; j < Nsedgelist; j++) { /* j: sedgelist counter */
418     for (ita = 0; ita < nta; ita++) {
419       /* vfrom */
420       net  = sedgelist[k];
421       idx  = sedgelist[k + 1];
422       gidx = network->cloneshared->subnet[net].vStart + idx; /* global index of the vertex net.idx before merging shared vertices */
423       PetscCall(PetscTableFind(svtas[ita], gidx + 1, &idx_from));
424 
425       /* vto */
426       net  = sedgelist[k + 2];
427       idx  = sedgelist[k + 3];
428       gidx = network->cloneshared->subnet[net].vStart + idx;
429       PetscCall(PetscTableFind(svtas[ita], gidx + 1, &idx_to));
430 
431       if (idx_from || idx_to) { /* vfrom or vto is on table svtas[ita] */
432         idx_from--;
433         idx_to--;
434         if (idx_from < 0) { /* vto is on svtas[ita] */
435           PetscCall(TableAddSVtx(network, sedgelist, k, svtas[ita], &tdata[ita], ta2sv[ita]));
436           break;
437         } else if (idx_to < 0) {
438           PetscCall(TableAddSVtx(network, sedgelist, k + 2, svtas[ita], &tdata[ita], ta2sv[ita]));
439           break;
440         }
441       }
442     }
443 
444     if (ita == nta) {
445       PetscCall(PetscTableCreate(2 * Nsedgelist, network->cloneshared->NVertices + 1, &svtas[nta]));
446       PetscCall(PetscMalloc1(2 * Nsedgelist, &ta2sv[nta]));
447 
448       PetscCall(TableAddSVtx(network, sedgelist, k, svtas[nta], &tdata[nta], ta2sv[nta]));
449       PetscCall(TableAddSVtx(network, sedgelist, k + 2, svtas[nta], &tdata[nta], ta2sv[nta]));
450       nta++;
451     }
452     k += 4;
453   }
454 
455   /* (2) Create svtable for query shared vertices using gidx */
456   PetscCall(PetscTableCreate(nta, network->cloneshared->NVertices + 1, &network->cloneshared->svtable));
457 
458   /* (3) Construct svtx from svtas
459    svtx: array of SVtx: sv[0]=(net[0],idx[0]) to vertices sv[k], k=1,...,n-1. */
460   PetscCall(PetscMalloc1(nta, &svtx));
461   for (nsv = 0; nsv < nta; nsv++) {
462     /* for a single svtx, put shared vertices in ascending order of gidx */
463     PetscCall(PetscTableGetCount(svtas[nsv], &n));
464     PetscCall(PetscCalloc1(2 * n, &sv));
465     PetscCall(PetscMalloc3(n, &gidx_tmp, n, &net_tmp, n, &idx_tmp));
466     svtx[nsv].sv   = sv;
467     svtx[nsv].n    = n;
468     svtx[nsv].gidx = network->cloneshared->NVertices; /* initialization */
469 
470     PetscCall(PetscTableGetHeadPosition(svtas[nsv], &ppos));
471     for (k = 0; k < n; k++) { /* gidx is sorted in ascending order */
472       PetscCall(PetscTableGetNext(svtas[nsv], &ppos, &gidx, &i));
473       gidx--;
474       i--;
475       j           = ta2sv[nsv][i];    /* maps i to index of sedgelist */
476       net_tmp[k]  = sedgelist[j];     /* subnet number */
477       idx_tmp[k]  = sedgelist[j + 1]; /* index on the subnet */
478       gidx_tmp[k] = gidx;             /* gidx in un-merged dmnetwork */
479     }
480 
481     /* current implementation requires sv[]=[net,idx] in ascending order of its gidx in un-merged dmnetwork */
482     PetscCall(PetscSortIntWithArrayPair(n, gidx_tmp, net_tmp, idx_tmp));
483     svtx[nsv].gidx = gidx_tmp[0]; /* = min(gidx) */
484     for (k = 0; k < n; k++) {
485       sv[2 * k]     = net_tmp[k];
486       sv[2 * k + 1] = idx_tmp[k];
487     }
488     PetscCall(PetscFree3(gidx_tmp, net_tmp, idx_tmp));
489 
490     /* Setup svtable for query shared vertices */
491     PetscCall(PetscTableAdd(network->cloneshared->svtable, svtx[nsv].gidx + 1, nsv + 1, INSERT_VALUES));
492   }
493 
494   for (j = 0; j < nta; j++) {
495     PetscCall(PetscTableDestroy(&svtas[j]));
496     PetscCall(PetscFree(ta2sv[j]));
497   }
498   PetscCall(PetscFree3(svtas, tdata, ta2sv));
499 
500   network->cloneshared->Nsvtx = nta;
501   network->cloneshared->svtx  = svtx;
502   PetscFunctionReturn(0);
503 }
504 
505 /*
506   GetEdgelist_Coupling - Get an integrated edgelist for dmplex from user-provided subnet[].edgelist when subnets are coupled by shared vertices
507 
508   Input Parameters:
509 . dm - the dmnetwork object
510 
511    Output Parameters:
512 +  edges - the integrated edgelist for dmplex
513 -  nmerged_ptr - num of vertices being merged
514 */
515 static PetscErrorCode GetEdgelist_Coupling(DM dm, PetscInt *edges, PetscInt *nmerged_ptr)
516 {
517   MPI_Comm    comm;
518   PetscMPIInt size, rank, *recvcounts = NULL, *displs = NULL;
519   DM_Network *network = (DM_Network *)dm->data;
520   PetscInt    i, j, ctr, np;
521   PetscInt   *vidxlTog, Nsv, Nsubnet = network->cloneshared->Nsubnet;
522   PetscInt   *sedgelist = network->cloneshared->sedgelist;
523   PetscInt    net, idx, gidx, nmerged, *vrange, gidx_from, net_from, sv_idx;
524   SVtxType    svtype = SVNONE;
525   SVtx       *svtx;
526 
527   PetscFunctionBegin;
528   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
529   PetscCallMPI(MPI_Comm_rank(comm, &rank));
530   PetscCallMPI(MPI_Comm_size(comm, &size));
531 
532   /* (1) Create global svtx[] from sedgelist */
533   /* --------------------------------------- */
534   PetscCall(SharedVtxCreate(dm, network->cloneshared->Nsvtx, sedgelist));
535   Nsv  = network->cloneshared->Nsvtx;
536   svtx = network->cloneshared->svtx;
537 
538   /* (2) Merge shared vto vertices to their vfrom vertex with same global vertex index (gidx) */
539   /* --------------------------------------------------------------------------------------- */
540   /* (2.1) compute vrage[rank]: global index of 1st local vertex in proc[rank] */
541   PetscCall(PetscMalloc4(size + 1, &vrange, size, &displs, size, &recvcounts, network->cloneshared->nVertices, &vidxlTog));
542   for (i = 0; i < size; i++) {
543     displs[i]     = i;
544     recvcounts[i] = 1;
545   }
546 
547   vrange[0] = 0;
548   PetscCallMPI(MPI_Allgatherv(&network->cloneshared->nVertices, 1, MPIU_INT, vrange + 1, recvcounts, displs, MPIU_INT, comm));
549   for (i = 2; i < size + 1; i++) vrange[i] += vrange[i - 1];
550 
551   /* (2.2) Create vidxlTog: maps UN-MERGED local vertex index i to global index gidx (plex, excluding ghost vertices) */
552   i                           = 0;
553   gidx                        = 0;
554   nmerged                     = 0; /* local num of merged vertices */
555   network->cloneshared->nsvtx = 0; /* local num of SVtx structs, including ghosts */
556   for (net = 0; net < Nsubnet; net++) {
557     for (idx = 0; idx < network->cloneshared->subnet[net].Nvtx; idx++) { /* Note: global subnet[net].Nvtx */
558       PetscCall(VtxGetInfo(Nsv, svtx, net, idx, &gidx_from, &svtype, &sv_idx));
559       if (svtype == SVTO) {
560         if (network->cloneshared->subnet[net].nvtx) { /* this proc owns sv_to */
561           net_from = svtx[sv_idx].sv[0];              /* subnet number of its shared vertex */
562           if (network->cloneshared->subnet[net_from].nvtx == 0) {
563             /* this proc does not own v_from, thus a ghost local vertex */
564             network->cloneshared->nsvtx++;
565           }
566           vidxlTog[i++] = gidx_from; /* gidx before merging! Bug??? */
567           nmerged++;                 /* a shared vertex -- merged */
568         }
569       } else {
570         if (svtype == SVFROM && network->cloneshared->subnet[net].nvtx) {
571           /* this proc owns this v_from, a new local shared vertex */
572           network->cloneshared->nsvtx++;
573         }
574         if (network->cloneshared->subnet[net].nvtx) vidxlTog[i++] = gidx;
575         gidx++;
576       }
577     }
578   }
579 #if defined(PETSC_USE_DEBUG)
580   PetscCheck(i == network->cloneshared->nVertices, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "%" PetscInt_FMT " != %" PetscInt_FMT " nVertices", i, network->cloneshared->nVertices);
581 #endif
582 
583   /* (2.3) Shared vertices in the subnetworks are merged, update global NVertices: np = sum(local nmerged) */
584   PetscCallMPI(MPI_Allreduce(&nmerged, &np, 1, MPIU_INT, MPI_SUM, comm));
585   network->cloneshared->NVertices -= np;
586 
587   ctr = 0;
588   for (net = 0; net < Nsubnet; net++) {
589     for (j = 0; j < network->cloneshared->subnet[net].nedge; j++) {
590       /* vfrom: */
591       i              = network->cloneshared->subnet[net].edgelist[2 * j] + (network->cloneshared->subnet[net].vStart - vrange[rank]);
592       edges[2 * ctr] = vidxlTog[i];
593 
594       /* vto */
595       i                  = network->cloneshared->subnet[net].edgelist[2 * j + 1] + (network->cloneshared->subnet[net].vStart - vrange[rank]);
596       edges[2 * ctr + 1] = vidxlTog[i];
597       ctr++;
598     }
599   }
600   PetscCall(PetscFree4(vrange, displs, recvcounts, vidxlTog));
601   PetscCall(PetscFree(sedgelist)); /* created in DMNetworkAddSharedVertices() */
602 
603   *nmerged_ptr = nmerged;
604   PetscFunctionReturn(0);
605 }
606 
607 PetscErrorCode DMNetworkInitializeNonTopological(DM dm)
608 {
609   DM_Network *network = (DM_Network *)dm->data;
610   PetscInt    p, pStart = network->cloneshared->pStart, pEnd = network->cloneshared->pEnd;
611   MPI_Comm    comm;
612 
613   PetscFunctionBegin;
614   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
615 
616   PetscCall(PetscSectionCreate(comm, &network->DataSection));
617   PetscCall(PetscSectionCreate(comm, &network->DofSection));
618   PetscCall(PetscSectionSetChart(network->DataSection, pStart, pEnd));
619   PetscCall(PetscSectionSetChart(network->DofSection, pStart, pEnd));
620 
621   PetscCall(DMNetworkInitializeHeaderComponentData(dm));
622 
623   for (p = 0; p < pEnd - pStart; p++) {
624     network->header[p].ndata           = 0;
625     network->header[p].offset[0]       = 0;
626     network->header[p].offsetvarrel[0] = 0;
627     PetscCall(PetscSectionAddDof(network->DataSection, p, network->header[p].hsize));
628   }
629   PetscFunctionReturn(0);
630 }
631 
632 /*@
633   DMNetworkLayoutSetUp - Sets up the bare layout (graph) for the network
634 
635   Not Collective
636 
637   Input Parameters:
638 . dm - the dmnetwork object
639 
640   Notes:
641   This routine should be called after the network sizes and edgelists have been provided. It creates
642   the bare layout of the network and sets up the network to begin insertion of components.
643 
644   All the components should be registered before calling this routine.
645 
646   Level: beginner
647 
648 .seealso: `DMNetworkSetNumSubNetworks()`, `DMNetworkAddSubnetwork()`
649 @*/
650 PetscErrorCode DMNetworkLayoutSetUp(DM dm)
651 {
652   DM_Network     *network = (DM_Network *)dm->data;
653   PetscInt        i, j, ctr, Nsubnet = network->cloneshared->Nsubnet, np, *edges, *subnetvtx, *subnetedge, e, v, vfrom, vto, net, globaledgeoff;
654   const PetscInt *cone;
655   MPI_Comm        comm;
656   PetscMPIInt     size;
657   PetscSection    sectiong;
658   PetscInt        nmerged = 0;
659 
660   PetscFunctionBegin;
661   PetscCall(PetscLogEventBegin(DMNetwork_LayoutSetUp, dm, 0, 0, 0));
662   PetscCheck(network->cloneshared->nsubnet == Nsubnet, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Must call DMNetworkAddSubnetwork() %" PetscInt_FMT " times", Nsubnet);
663 
664   /* This implementation requires user input each subnet by a single processor when Nsubnet>1, thus subnet[net].nvtx=subnet[net].Nvtx when net>0 */
665   for (net = 1; net < Nsubnet; net++) {
666     if (network->cloneshared->subnet[net].nvtx)
667       PetscCheck(network->cloneshared->subnet[net].nvtx == network->cloneshared->subnet[net].Nvtx, PETSC_COMM_SELF, PETSC_ERR_SUP, "subnetwork %" PetscInt_FMT " local num of vertices %" PetscInt_FMT " != %" PetscInt_FMT " global num", net,
668                  network->cloneshared->subnet[net].nvtx, network->cloneshared->subnet[net].Nvtx);
669   }
670 
671   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
672   PetscCall(MPI_Comm_size(comm, &size));
673 
674   /* Create LOCAL edgelist in global vertex ordering for the network by concatenating local input edgelists of the subnetworks */
675   PetscCall(PetscCalloc1(2 * network->cloneshared->nEdges, &edges));
676 
677   if (network->cloneshared->Nsvtx) { /* subnetworks are coupled via shared vertices */
678     PetscCall(GetEdgelist_Coupling(dm, edges, &nmerged));
679   } else { /* subnetworks are not coupled */
680     /* Create a 0-size svtable for query shared vertices */
681     PetscCall(PetscTableCreate(0, network->cloneshared->NVertices + 1, &network->cloneshared->svtable));
682     ctr = 0;
683     for (i = 0; i < Nsubnet; i++) {
684       for (j = 0; j < network->cloneshared->subnet[i].nedge; j++) {
685         edges[2 * ctr]     = network->cloneshared->subnet[i].vStart + network->cloneshared->subnet[i].edgelist[2 * j];
686         edges[2 * ctr + 1] = network->cloneshared->subnet[i].vStart + network->cloneshared->subnet[i].edgelist[2 * j + 1];
687         ctr++;
688       }
689     }
690   }
691 
692   /* Create network->plex; One dimensional network, numCorners=2 */
693   PetscCall(DMCreate(comm, &network->plex));
694   PetscCall(DMSetType(network->plex, DMPLEX));
695   PetscCall(DMSetDimension(network->plex, 1));
696 
697   if (size == 1) PetscCall(DMPlexBuildFromCellList(network->plex, network->cloneshared->nEdges, PETSC_DECIDE, 2, edges));
698   else PetscCall(DMPlexBuildFromCellListParallel(network->plex, network->cloneshared->nEdges, PETSC_DECIDE, PETSC_DECIDE, 2, edges, NULL, NULL));
699 
700   PetscCall(DMPlexGetChart(network->plex, &network->cloneshared->pStart, &network->cloneshared->pEnd));
701   PetscCall(DMPlexGetHeightStratum(network->plex, 0, &network->cloneshared->eStart, &network->cloneshared->eEnd));
702   PetscCall(DMPlexGetHeightStratum(network->plex, 1, &network->cloneshared->vStart, &network->cloneshared->vEnd));
703   np = network->cloneshared->pEnd - network->cloneshared->pStart;
704   PetscCall(PetscCalloc2(np, &network->header, np, &network->cvalue));
705 
706   /* Create edge and vertex arrays for the subnetworks
707      This implementation assumes that DMNetwork reads
708      (1) a single subnetwork in parallel; or
709      (2) n subnetworks using n processors, one subnetwork/processor.
710   */
711   PetscCall(PetscCalloc2(network->cloneshared->nEdges, &subnetedge, network->cloneshared->nVertices + network->cloneshared->nsvtx, &subnetvtx)); /* Maps local edge/vertex to local subnetwork's edge/vertex */
712   network->cloneshared->subnetedge = subnetedge;
713   network->cloneshared->subnetvtx  = subnetvtx;
714   for (j = 0; j < Nsubnet; j++) {
715     network->cloneshared->subnet[j].edges = subnetedge;
716     subnetedge += network->cloneshared->subnet[j].nedge;
717 
718     network->cloneshared->subnet[j].vertices = subnetvtx;
719     subnetvtx += network->cloneshared->subnet[j].nvtx;
720   }
721   network->cloneshared->svertices = subnetvtx;
722 
723   /* Get edge ownership */
724   np = network->cloneshared->eEnd - network->cloneshared->eStart;
725   PetscCallMPI(MPI_Scan(&np, &globaledgeoff, 1, MPIU_INT, MPI_SUM, comm));
726   globaledgeoff -= np;
727 
728   /* Setup local edge and vertex arrays for subnetworks */
729   e = 0;
730   for (i = 0; i < Nsubnet; i++) {
731     ctr = 0;
732     for (j = 0; j < network->cloneshared->subnet[i].nedge; j++) {
733       /* edge e */
734       network->header[e].index                 = e + globaledgeoff; /* Global edge index */
735       network->header[e].subnetid              = i;
736       network->cloneshared->subnet[i].edges[j] = e;
737 
738       /* connected vertices */
739       PetscCall(DMPlexGetCone(network->plex, e, &cone));
740 
741       /* vertex cone[0] */
742       v                           = cone[0];
743       network->header[v].index    = edges[2 * e]; /* Global vertex index */
744       network->header[v].subnetid = i;            /* Subnetwork id */
745       if (Nsubnet == 1) {
746         network->cloneshared->subnet[i].vertices[v - network->cloneshared->vStart] = v; /* user's subnet[].idx = petsc's v */
747       } else {
748         vfrom                                           = network->cloneshared->subnet[i].edgelist[2 * ctr]; /* =subnet[i].idx, Global index! */
749         network->cloneshared->subnet[i].vertices[vfrom] = v;                                                 /* user's subnet[].dix = petsc's v */
750       }
751 
752       /* vertex cone[1] */
753       v                           = cone[1];
754       network->header[v].index    = edges[2 * e + 1]; /* Global vertex index */
755       network->header[v].subnetid = i;                /* Subnetwork id */
756       if (Nsubnet == 1) {
757         network->cloneshared->subnet[i].vertices[v - network->cloneshared->vStart] = v; /* user's subnet[].idx = petsc's v */
758       } else {
759         vto                                           = network->cloneshared->subnet[i].edgelist[2 * ctr + 1]; /* =subnet[i].idx, Global index! */
760         network->cloneshared->subnet[i].vertices[vto] = v;                                                     /* user's subnet[].dix = petsc's v */
761       }
762 
763       e++;
764       ctr++;
765     }
766   }
767   PetscCall(PetscFree(edges));
768 
769   /* Set local vertex array for the subnetworks */
770   j = 0;
771   for (v = network->cloneshared->vStart; v < network->cloneshared->vEnd; v++) {
772     /* local shared vertex */
773     PetscCall(PetscTableFind(network->cloneshared->svtable, network->header[v].index + 1, &i));
774     if (i) network->cloneshared->svertices[j++] = v;
775   }
776 
777   /* Create a global section to be used by DMNetworkIsGhostVertex() which is a non-collective routine */
778   /* see snes_tutorials_network-ex1_4 */
779   PetscCall(DMGetGlobalSection(network->plex, &sectiong));
780   /* Initialize non-topological data structures  */
781   PetscCall(DMNetworkInitializeNonTopological(dm));
782   PetscCall(PetscLogEventEnd(DMNetwork_LayoutSetUp, dm, 0, 0, 0));
783   PetscFunctionReturn(0);
784 }
785 
786 /*@C
787   DMNetworkGetSubnetwork - Returns the information about a requested subnetwork
788 
789   Not collective
790 
791   Input Parameters:
792 + dm - the DM object
793 - netnum - the global index of the subnetwork
794 
795   Output Parameters:
796 + nv - number of vertices (local)
797 . ne - number of edges (local)
798 . vtx - local vertices of the subnetwork
799 - edge - local edges of the subnetwork
800 
801   Notes:
802     Cannot call this routine before DMNetworkLayoutSetup()
803 
804     The local vertices returned on each rank are determined by DMNetwork. The user does not have any control over what vertices are local.
805 
806   Level: intermediate
807 
808 .seealso: `DMNetworkCreate()`, `DMNetworkAddSubnetwork()`, `DMNetworkLayoutSetUp()`
809 @*/
810 PetscErrorCode DMNetworkGetSubnetwork(DM dm, PetscInt netnum, PetscInt *nv, PetscInt *ne, const PetscInt **vtx, const PetscInt **edge)
811 {
812   DM_Network *network = (DM_Network *)dm->data;
813 
814   PetscFunctionBegin;
815   PetscCheck(netnum < network->cloneshared->Nsubnet, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Subnet index %" PetscInt_FMT " exceeds the num of subnets %" PetscInt_FMT, netnum, network->cloneshared->Nsubnet);
816   if (nv) *nv = network->cloneshared->subnet[netnum].nvtx;
817   if (ne) *ne = network->cloneshared->subnet[netnum].nedge;
818   if (vtx) *vtx = network->cloneshared->subnet[netnum].vertices;
819   if (edge) *edge = network->cloneshared->subnet[netnum].edges;
820   PetscFunctionReturn(0);
821 }
822 
823 /*@
824   DMNetworkAddSharedVertices - Add shared vertices that connect two given subnetworks
825 
826   Collective on dm
827 
828   Input Parameters:
829 + dm - the dm object
830 . anetnum - first subnetwork global numbering returned by DMNetworkAddSubnetwork()
831 . bnetnum - second subnetwork global numbering returned by DMNetworkAddSubnetwork()
832 . nsvtx - number of vertices that are shared by the two subnetworks
833 . asvtx - vertex index in the first subnetwork
834 - bsvtx - vertex index in the second subnetwork
835 
836   Level: beginner
837 
838 .seealso: `DMNetworkCreate()`, `DMNetworkAddSubnetwork()`, `DMNetworkGetSharedVertices()`
839 @*/
840 PetscErrorCode DMNetworkAddSharedVertices(DM dm, PetscInt anetnum, PetscInt bnetnum, PetscInt nsvtx, PetscInt asvtx[], PetscInt bsvtx[])
841 {
842   DM_Network *network = (DM_Network *)dm->data;
843   PetscInt    i, nsubnet = network->cloneshared->Nsubnet, *sedgelist, Nsvtx = network->cloneshared->Nsvtx;
844 
845   PetscFunctionBegin;
846   PetscCheck(anetnum != bnetnum, PetscObjectComm((PetscObject)dm), PETSC_ERR_USER, "Subnetworks must have different netnum");
847   PetscCheck(anetnum >= 0 && bnetnum >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_USER, "netnum cannot be negative");
848   if (!Nsvtx) {
849     /* allocate network->sedgelist to hold at most 2*nsubnet pairs of shared vertices */
850     PetscCall(PetscMalloc1(2 * 4 * nsubnet, &network->cloneshared->sedgelist));
851   }
852 
853   sedgelist = network->cloneshared->sedgelist;
854   for (i = 0; i < nsvtx; i++) {
855     sedgelist[4 * Nsvtx]     = anetnum;
856     sedgelist[4 * Nsvtx + 1] = asvtx[i];
857     sedgelist[4 * Nsvtx + 2] = bnetnum;
858     sedgelist[4 * Nsvtx + 3] = bsvtx[i];
859     Nsvtx++;
860   }
861   PetscCheck(Nsvtx <= 2 * nsubnet, PETSC_COMM_SELF, PETSC_ERR_SUP, "allocate more space for coupling edgelist");
862   network->cloneshared->Nsvtx = Nsvtx;
863   PetscFunctionReturn(0);
864 }
865 
866 /*@C
867   DMNetworkGetSharedVertices - Returns the info for the shared vertices
868 
869   Not collective
870 
871   Input Parameter:
872 . dm - the DM object
873 
874   Output Parameters:
875 + nsv - number of local shared vertices
876 - svtx - local shared vertices
877 
878   Notes:
879   Cannot call this routine before DMNetworkLayoutSetup()
880 
881   Level: intermediate
882 
883 .seealso: `DMNetworkGetSubnetwork()`, `DMNetworkLayoutSetUp()`, `DMNetworkAddSharedVertices()`
884 @*/
885 PetscErrorCode DMNetworkGetSharedVertices(DM dm, PetscInt *nsv, const PetscInt **svtx)
886 {
887   DM_Network *net = (DM_Network *)dm->data;
888 
889   PetscFunctionBegin;
890   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
891   if (nsv) *nsv = net->cloneshared->nsvtx;
892   if (svtx) *svtx = net->cloneshared->svertices;
893   PetscFunctionReturn(0);
894 }
895 
896 /*@C
897   DMNetworkRegisterComponent - Registers the network component
898 
899   Logically collective on dm
900 
901   Input Parameters:
902 + dm - the network object
903 . name - the component name
904 - size - the storage size in bytes for this component data
905 
906    Output Parameters:
907 .  key - an integer key that defines the component
908 
909    Note:
910    This routine should be called by all processors before calling `DMNetworkLayoutSetup()`.
911 
912    Level: beginner
913 
914 .seealso: `DMNetworkCreate()`, `DMNetworkLayoutSetUp()`
915 @*/
916 PetscErrorCode DMNetworkRegisterComponent(DM dm, const char *name, size_t size, PetscInt *key)
917 {
918   DM_Network         *network   = (DM_Network *)dm->data;
919   DMNetworkComponent *component = NULL, *newcomponent = NULL;
920   PetscBool           flg = PETSC_FALSE;
921   PetscInt            i;
922 
923   PetscFunctionBegin;
924   if (!network->component) PetscCall(PetscCalloc1(network->max_comps_registered, &network->component));
925 
926   for (i = 0; i < network->ncomponent; i++) {
927     PetscCall(PetscStrcmp(network->component[i].name, name, &flg));
928     if (flg) {
929       *key = i;
930       PetscFunctionReturn(0);
931     }
932   }
933 
934   if (network->ncomponent == network->max_comps_registered) {
935     /* Reached max allowed so resize component */
936     network->max_comps_registered += 2;
937     PetscCall(PetscCalloc1(network->max_comps_registered, &newcomponent));
938     /* Copy over the previous component info */
939     for (i = 0; i < network->ncomponent; i++) {
940       PetscCall(PetscStrcpy(newcomponent[i].name, network->component[i].name));
941       newcomponent[i].size = network->component[i].size;
942     }
943     /* Free old one */
944     PetscCall(PetscFree(network->component));
945     /* Update pointer */
946     network->component = newcomponent;
947   }
948 
949   component = &network->component[network->ncomponent];
950 
951   PetscCall(PetscStrcpy(component->name, name));
952   component->size = size / sizeof(DMNetworkComponentGenericDataType);
953   *key            = network->ncomponent;
954   network->ncomponent++;
955   PetscFunctionReturn(0);
956 }
957 
958 /*@
959   DMNetworkGetNumVertices - Get the local and global number of vertices for the entire network.
960 
961   Not Collective
962 
963   Input Parameter:
964 . dm - the DMNetwork object
965 
966   Output Parameters:
967 + nVertices - the local number of vertices
968 - NVertices - the global number of vertices
969 
970   Level: beginner
971 
972 .seealso: `DMNetworkGetNumEdges()`
973 @*/
974 PetscErrorCode DMNetworkGetNumVertices(DM dm, PetscInt *nVertices, PetscInt *NVertices)
975 {
976   DM_Network *network = (DM_Network *)dm->data;
977 
978   PetscFunctionBegin;
979   PetscValidHeaderSpecificType(dm, DM_CLASSID, 1, DMNETWORK);
980   if (nVertices) {
981     PetscValidIntPointer(nVertices, 2);
982     *nVertices = network->cloneshared->nVertices;
983   }
984   if (NVertices) {
985     PetscValidIntPointer(NVertices, 3);
986     *NVertices = network->cloneshared->NVertices;
987   }
988   PetscFunctionReturn(0);
989 }
990 
991 /*@
992   DMNetworkGetNumEdges - Get the local and global number of edges for the entire network.
993 
994   Not Collective
995 
996   Input Parameter:
997 . dm - the DMNetwork object
998 
999   Output Parameters:
1000 + nEdges - the local number of edges
1001 -  NEdges - the global number of edges
1002 
1003   Level: beginner
1004 
1005 .seealso: `DMNetworkGetNumVertices()`
1006 @*/
1007 PetscErrorCode DMNetworkGetNumEdges(DM dm, PetscInt *nEdges, PetscInt *NEdges)
1008 {
1009   DM_Network *network = (DM_Network *)dm->data;
1010 
1011   PetscFunctionBegin;
1012   PetscValidHeaderSpecificType(dm, DM_CLASSID, 1, DMNETWORK);
1013   if (nEdges) {
1014     PetscValidIntPointer(nEdges, 2);
1015     *nEdges = network->cloneshared->nEdges;
1016   }
1017   if (NEdges) {
1018     PetscValidIntPointer(NEdges, 3);
1019     *NEdges = network->cloneshared->NEdges;
1020   }
1021   PetscFunctionReturn(0);
1022 }
1023 
1024 /*@
1025   DMNetworkGetVertexRange - Get the bounds [start, end) for the local vertices
1026 
1027   Not Collective
1028 
1029   Input Parameter:
1030 . dm - the DMNetwork object
1031 
1032   Output Parameters:
1033 + vStart - the first vertex point
1034 - vEnd - one beyond the last vertex point
1035 
1036   Level: beginner
1037 
1038 .seealso: `DMNetworkGetEdgeRange()`
1039 @*/
1040 PetscErrorCode DMNetworkGetVertexRange(DM dm, PetscInt *vStart, PetscInt *vEnd)
1041 {
1042   DM_Network *network = (DM_Network *)dm->data;
1043 
1044   PetscFunctionBegin;
1045   if (vStart) *vStart = network->cloneshared->vStart;
1046   if (vEnd) *vEnd = network->cloneshared->vEnd;
1047   PetscFunctionReturn(0);
1048 }
1049 
1050 /*@
1051   DMNetworkGetEdgeRange - Get the bounds [start, end) for the local edges
1052 
1053   Not Collective
1054 
1055   Input Parameter:
1056 . dm - the DMNetwork object
1057 
1058   Output Parameters:
1059 + eStart - The first edge point
1060 - eEnd - One beyond the last edge point
1061 
1062   Level: beginner
1063 
1064 .seealso: `DMNetworkGetVertexRange()`
1065 @*/
1066 PetscErrorCode DMNetworkGetEdgeRange(DM dm, PetscInt *eStart, PetscInt *eEnd)
1067 {
1068   DM_Network *network = (DM_Network *)dm->data;
1069 
1070   PetscFunctionBegin;
1071   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1072   if (eStart) *eStart = network->cloneshared->eStart;
1073   if (eEnd) *eEnd = network->cloneshared->eEnd;
1074   PetscFunctionReturn(0);
1075 }
1076 
1077 PetscErrorCode DMNetworkGetIndex(DM dm, PetscInt p, PetscInt *index)
1078 {
1079   DM_Network *network = (DM_Network *)dm->data;
1080 
1081   PetscFunctionBegin;
1082   if (network->header) {
1083     *index = network->header[p].index;
1084   } else {
1085     PetscInt                 offsetp;
1086     DMNetworkComponentHeader header;
1087 
1088     PetscCall(PetscSectionGetOffset(network->DataSection, p, &offsetp));
1089     header = (DMNetworkComponentHeader)(network->componentdataarray + offsetp);
1090     *index = header->index;
1091   }
1092   PetscFunctionReturn(0);
1093 }
1094 
1095 PetscErrorCode DMNetworkGetSubnetID(DM dm, PetscInt p, PetscInt *subnetid)
1096 {
1097   DM_Network *network = (DM_Network *)dm->data;
1098 
1099   PetscFunctionBegin;
1100   if (network->header) {
1101     *subnetid = network->header[p].subnetid;
1102   } else {
1103     PetscInt                 offsetp;
1104     DMNetworkComponentHeader header;
1105 
1106     PetscCall(PetscSectionGetOffset(network->DataSection, p, &offsetp));
1107     header    = (DMNetworkComponentHeader)(network->componentdataarray + offsetp);
1108     *subnetid = header->subnetid;
1109   }
1110   PetscFunctionReturn(0);
1111 }
1112 
1113 /*@
1114   DMNetworkGetGlobalEdgeIndex - Get the global numbering for the edge on the network
1115 
1116   Not Collective
1117 
1118   Input Parameters:
1119 + dm - DMNetwork object
1120 - p - edge point
1121 
1122   Output Parameters:
1123 . index - the global numbering for the edge
1124 
1125   Level: intermediate
1126 
1127 .seealso: `DMNetworkGetGlobalVertexIndex()`
1128 @*/
1129 PetscErrorCode DMNetworkGetGlobalEdgeIndex(DM dm, PetscInt p, PetscInt *index)
1130 {
1131   PetscFunctionBegin;
1132   PetscCall(DMNetworkGetIndex(dm, p, index));
1133   PetscFunctionReturn(0);
1134 }
1135 
1136 /*@
1137   DMNetworkGetGlobalVertexIndex - Get the global numbering for the vertex on the network
1138 
1139   Not Collective
1140 
1141   Input Parameters:
1142 + dm - DMNetwork object
1143 - p  - vertex point
1144 
1145   Output Parameters:
1146 . index - the global numbering for the vertex
1147 
1148   Level: intermediate
1149 
1150 .seealso: `DMNetworkGetGlobalEdgeIndex()`, `DMNetworkGetLocalVertexIndex()`
1151 @*/
1152 PetscErrorCode DMNetworkGetGlobalVertexIndex(DM dm, PetscInt p, PetscInt *index)
1153 {
1154   PetscFunctionBegin;
1155   PetscCall(DMNetworkGetIndex(dm, p, index));
1156   PetscFunctionReturn(0);
1157 }
1158 
1159 /*@
1160   DMNetworkGetNumComponents - Get the number of components at a vertex/edge
1161 
1162   Not Collective
1163 
1164   Input Parameters:
1165 + dm - the DMNetwork object
1166 - p - vertex/edge point
1167 
1168   Output Parameters:
1169 . numcomponents - Number of components at the vertex/edge
1170 
1171   Level: beginner
1172 
1173 .seealso: `DMNetworkRegisterComponent()`, `DMNetworkAddComponent()`
1174 @*/
1175 PetscErrorCode DMNetworkGetNumComponents(DM dm, PetscInt p, PetscInt *numcomponents)
1176 {
1177   PetscInt    offset;
1178   DM_Network *network = (DM_Network *)dm->data;
1179 
1180   PetscFunctionBegin;
1181   PetscCall(PetscSectionGetOffset(network->DataSection, p, &offset));
1182   *numcomponents = ((DMNetworkComponentHeader)(network->componentdataarray + offset))->ndata;
1183   PetscFunctionReturn(0);
1184 }
1185 
1186 /*@
1187   DMNetworkGetLocalVecOffset - Get the offset for accessing the variables associated with a component at the given vertex/edge from the local vector
1188 
1189   Not Collective
1190 
1191   Input Parameters:
1192 + dm - the DMNetwork object
1193 . p - the edge or vertex point
1194 - compnum - component number; use ALL_COMPONENTS if no specific component is requested
1195 
1196   Output Parameters:
1197 . offset - the local offset
1198 
1199   Notes:
1200     These offsets can be passed to MatSetValuesLocal() for matrices obtained with DMCreateMatrix().
1201 
1202     For vectors obtained with DMCreateLocalVector() the offsets can be used with VecSetValues().
1203 
1204     For vectors obtained with DMCreateLocalVector() and the array obtained with VecGetArray(vec,&array) you can access or set
1205     the vector values with array[offset].
1206 
1207     For vectors obtained with DMCreateGlobalVector() the offsets can be used with VecSetValuesLocal().
1208 
1209   Level: intermediate
1210 
1211 .seealso: `DMGetLocalVector()`, `DMNetworkGetComponent()`, `DMNetworkGetGlobalVecOffset()`, `DMCreateGlobalVector()`, `VecGetArray()`, `VecSetValuesLocal()`, `MatSetValuesLocal()`
1212 @*/
1213 PetscErrorCode DMNetworkGetLocalVecOffset(DM dm, PetscInt p, PetscInt compnum, PetscInt *offset)
1214 {
1215   DM_Network              *network = (DM_Network *)dm->data;
1216   PetscInt                 offsetp, offsetd;
1217   DMNetworkComponentHeader header;
1218 
1219   PetscFunctionBegin;
1220   PetscCall(PetscSectionGetOffset(network->plex->localSection, p, &offsetp));
1221   if (compnum == ALL_COMPONENTS) {
1222     *offset = offsetp;
1223     PetscFunctionReturn(0);
1224   }
1225 
1226   PetscCall(PetscSectionGetOffset(network->DataSection, p, &offsetd));
1227   header  = (DMNetworkComponentHeader)(network->componentdataarray + offsetd);
1228   *offset = offsetp + header->offsetvarrel[compnum];
1229   PetscFunctionReturn(0);
1230 }
1231 
1232 /*@
1233   DMNetworkGetGlobalVecOffset - Get the global offset for accessing the variables associated with a component for the given vertex/edge from the global vector
1234 
1235   Not Collective
1236 
1237   Input Parameters:
1238 + dm - the DMNetwork object
1239 . p - the edge or vertex point
1240 - compnum - component number; use ALL_COMPONENTS if no specific component is requested
1241 
1242   Output Parameters:
1243 . offsetg - the global offset
1244 
1245   Notes:
1246     These offsets can be passed to MatSetValues() for matrices obtained with DMCreateMatrix().
1247 
1248     For vectors obtained with DMCreateGlobalVector() the offsets can be used with VecSetValues().
1249 
1250     For vectors obtained with DMCreateGlobalVector() and the array obtained with VecGetArray(vec,&array) you can access or set
1251     the vector values with array[offset - rstart] where restart is obtained with VecGetOwnershipRange(v,&rstart,NULL);
1252 
1253   Level: intermediate
1254 
1255 .seealso: `DMNetworkGetLocalVecOffset()`, `DMGetGlobalVector()`, `DMNetworkGetComponent()`, `DMCreateGlobalVector()`, `VecGetArray()`, `VecSetValues()`, `MatSetValues()`
1256 @*/
1257 PetscErrorCode DMNetworkGetGlobalVecOffset(DM dm, PetscInt p, PetscInt compnum, PetscInt *offsetg)
1258 {
1259   DM_Network              *network = (DM_Network *)dm->data;
1260   PetscInt                 offsetp, offsetd;
1261   DMNetworkComponentHeader header;
1262 
1263   PetscFunctionBegin;
1264   PetscCall(PetscSectionGetOffset(network->plex->globalSection, p, &offsetp));
1265   if (offsetp < 0) offsetp = -(offsetp + 1); /* Convert to actual global offset for ghost vertex */
1266 
1267   if (compnum == ALL_COMPONENTS) {
1268     *offsetg = offsetp;
1269     PetscFunctionReturn(0);
1270   }
1271   PetscCall(PetscSectionGetOffset(network->DataSection, p, &offsetd));
1272   header   = (DMNetworkComponentHeader)(network->componentdataarray + offsetd);
1273   *offsetg = offsetp + header->offsetvarrel[compnum];
1274   PetscFunctionReturn(0);
1275 }
1276 
1277 /*@
1278   DMNetworkGetEdgeOffset - Get the offset for accessing the variables associated with the given edge from the local subvector
1279 
1280   Not Collective
1281 
1282   Input Parameters:
1283 + dm - the DMNetwork object
1284 - p - the edge point
1285 
1286   Output Parameters:
1287 . offset - the offset
1288 
1289   Level: intermediate
1290 
1291 .seealso: `DMNetworkGetLocalVecOffset()`, `DMGetLocalVector()`
1292 @*/
1293 PetscErrorCode DMNetworkGetEdgeOffset(DM dm, PetscInt p, PetscInt *offset)
1294 {
1295   DM_Network *network = (DM_Network *)dm->data;
1296 
1297   PetscFunctionBegin;
1298   PetscCall(PetscSectionGetOffset(network->edge.DofSection, p, offset));
1299   PetscFunctionReturn(0);
1300 }
1301 
1302 /*@
1303   DMNetworkGetVertexOffset - Get the offset for accessing the variables associated with the given vertex from the local subvector
1304 
1305   Not Collective
1306 
1307   Input Parameters:
1308 + dm - the DMNetwork object
1309 - p - the vertex point
1310 
1311   Output Parameters:
1312 . offset - the offset
1313 
1314   Level: intermediate
1315 
1316 .seealso: `DMNetworkGetEdgeOffset()`, `DMGetLocalVector()`
1317 @*/
1318 PetscErrorCode DMNetworkGetVertexOffset(DM dm, PetscInt p, PetscInt *offset)
1319 {
1320   DM_Network *network = (DM_Network *)dm->data;
1321 
1322   PetscFunctionBegin;
1323   p -= network->cloneshared->vStart;
1324   PetscCall(PetscSectionGetOffset(network->vertex.DofSection, p, offset));
1325   PetscFunctionReturn(0);
1326 }
1327 
1328 /*@
1329   DMNetworkAddComponent - Adds a network component and number of variables at the given point (vertex/edge)
1330 
1331   Collective on dm
1332 
1333   Input Parameters:
1334 + dm - the DMNetwork
1335 . p - the vertex/edge point. These points are local indices provided by DMNetworkGetSubnetwork()
1336 . componentkey - component key returned while registering the component with DMNetworkRegisterComponent()
1337 . compvalue - pointer to the data structure for the component, or NULL if the component does not require data, this data is not copied so you cannot
1338               free this space until after DMSetUp() is called.
1339 - nvar - number of variables for the component at the vertex/edge point, zero if the component does not introduce any degrees of freedom at the point
1340 
1341   Notes:
1342     The owning rank and any other ranks that have this point as a ghost location must call this routine to add a component and number of variables in the same order at the given point.
1343 
1344     DMNetworkLayoutSetUp() must be called before this routine.
1345 
1346   Developer Notes:
1347      The requirement that all the ranks with access to a vertex (as owner or as ghost) add all the components comes from a limitation of the underlying implementation based on DMPLEX.
1348   Level: beginner
1349 
1350 .seealso: `DMNetworkGetComponent()`, `DMNetworkGetSubnetwork()`, `DMNetworkIsGhostVertex()`, `DMNetworkLayoutSetUp()`
1351 @*/
1352 PetscErrorCode DMNetworkAddComponent(DM dm, PetscInt p, PetscInt componentkey, void *compvalue, PetscInt nvar)
1353 {
1354   DM_Network              *network   = (DM_Network *)dm->data;
1355   DMNetworkComponent      *component = &network->component[componentkey];
1356   DMNetworkComponentHeader header;
1357   DMNetworkComponentValue  cvalue;
1358   PetscInt                 compnum;
1359   PetscInt                *compsize, *compkey, *compoffset, *compnvar, *compoffsetvarrel;
1360   void                   **compdata;
1361 
1362   PetscFunctionBegin;
1363   PetscCheck(componentkey >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "componentkey %" PetscInt_FMT " cannot be negative. Input a component key returned while registering the component with DMNetworkRegisterComponent()", componentkey);
1364   PetscCheck(network->componentsetup == PETSC_FALSE, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "The network has already finalized the components. No new components can be added.");
1365   /* The owning rank and all ghost ranks add nvar */
1366   PetscCall(PetscSectionAddDof(network->DofSection, p, nvar));
1367 
1368   /* The owning rank and all ghost ranks add a component, including compvalue=NULL */
1369   header = &network->header[p];
1370   cvalue = &network->cvalue[p];
1371   if (header->ndata == header->maxcomps) {
1372     PetscInt additional_size;
1373 
1374     /* Reached limit so resize header component arrays */
1375     header->maxcomps += 2;
1376 
1377     /* Allocate arrays for component information and value */
1378     PetscCall(PetscCalloc5(header->maxcomps, &compsize, header->maxcomps, &compkey, header->maxcomps, &compoffset, header->maxcomps, &compnvar, header->maxcomps, &compoffsetvarrel));
1379     PetscCall(PetscMalloc1(header->maxcomps, &compdata));
1380 
1381     /* Recalculate header size */
1382     header->hsize = sizeof(struct _p_DMNetworkComponentHeader) + 5 * header->maxcomps * sizeof(PetscInt);
1383 
1384     header->hsize /= sizeof(DMNetworkComponentGenericDataType);
1385 
1386     /* Copy over component info */
1387     PetscCall(PetscMemcpy(compsize, header->size, header->ndata * sizeof(PetscInt)));
1388     PetscCall(PetscMemcpy(compkey, header->key, header->ndata * sizeof(PetscInt)));
1389     PetscCall(PetscMemcpy(compoffset, header->offset, header->ndata * sizeof(PetscInt)));
1390     PetscCall(PetscMemcpy(compnvar, header->nvar, header->ndata * sizeof(PetscInt)));
1391     PetscCall(PetscMemcpy(compoffsetvarrel, header->offsetvarrel, header->ndata * sizeof(PetscInt)));
1392 
1393     /* Copy over component data pointers */
1394     PetscCall(PetscMemcpy(compdata, cvalue->data, header->ndata * sizeof(void *)));
1395 
1396     /* Free old arrays */
1397     PetscCall(PetscFree5(header->size, header->key, header->offset, header->nvar, header->offsetvarrel));
1398     PetscCall(PetscFree(cvalue->data));
1399 
1400     /* Update pointers */
1401     header->size         = compsize;
1402     header->key          = compkey;
1403     header->offset       = compoffset;
1404     header->nvar         = compnvar;
1405     header->offsetvarrel = compoffsetvarrel;
1406 
1407     cvalue->data = compdata;
1408 
1409     /* Update DataSection Dofs */
1410     /* The dofs for datasection point p equals sizeof the header (i.e. header->hsize) + sizes of the components added at point p. With the resizing of the header, we need to update the dofs for point p. Hence, we add the extra size added for the header */
1411     additional_size = (5 * (header->maxcomps - header->ndata) * sizeof(PetscInt)) / sizeof(DMNetworkComponentGenericDataType);
1412     PetscCall(PetscSectionAddDof(network->DataSection, p, additional_size));
1413   }
1414   header = &network->header[p];
1415   cvalue = &network->cvalue[p];
1416 
1417   compnum = header->ndata;
1418 
1419   header->size[compnum] = component->size;
1420   PetscCall(PetscSectionAddDof(network->DataSection, p, component->size));
1421   header->key[compnum] = componentkey;
1422   if (compnum != 0) header->offset[compnum] = header->offset[compnum - 1] + header->size[compnum - 1];
1423   cvalue->data[compnum] = (void *)compvalue;
1424 
1425   /* variables */
1426   header->nvar[compnum] += nvar;
1427   if (compnum != 0) header->offsetvarrel[compnum] = header->offsetvarrel[compnum - 1] + header->nvar[compnum - 1];
1428 
1429   header->ndata++;
1430   PetscFunctionReturn(0);
1431 }
1432 
1433 /*@
1434   DMNetworkGetComponent - Gets the component key, the component data, and the number of variables at a given network point
1435 
1436   Not Collective
1437 
1438   Input Parameters:
1439 + dm - the DMNetwork object
1440 . p - vertex/edge point
1441 - compnum - component number; use ALL_COMPONENTS if sum up all the components
1442 
1443   Output Parameters:
1444 + compkey - the key obtained when registering the component (use NULL if not required)
1445 . component - the component data (use NULL if not required)
1446 - nvar  - number of variables (use NULL if not required)
1447 
1448   Level: beginner
1449 
1450 .seealso: `DMNetworkAddComponent()`, `DMNetworkGetNumComponents()`
1451 @*/
1452 PetscErrorCode DMNetworkGetComponent(DM dm, PetscInt p, PetscInt compnum, PetscInt *compkey, void **component, PetscInt *nvar)
1453 {
1454   DM_Network              *network = (DM_Network *)dm->data;
1455   PetscInt                 offset  = 0;
1456   DMNetworkComponentHeader header;
1457 
1458   PetscFunctionBegin;
1459   if (compnum == ALL_COMPONENTS) {
1460     PetscCall(PetscSectionGetDof(network->DofSection, p, nvar));
1461     PetscFunctionReturn(0);
1462   }
1463 
1464   PetscCall(PetscSectionGetOffset(network->DataSection, p, &offset));
1465   header = (DMNetworkComponentHeader)(network->componentdataarray + offset);
1466 
1467   if (compnum >= 0) {
1468     if (compkey) *compkey = header->key[compnum];
1469     if (component) {
1470       offset += header->hsize + header->offset[compnum];
1471       *component = network->componentdataarray + offset;
1472     }
1473   }
1474 
1475   if (nvar) *nvar = header->nvar[compnum];
1476 
1477   PetscFunctionReturn(0);
1478 }
1479 
1480 /*
1481  Sets up the array that holds the data for all components and its associated section.
1482  It copies the data for all components in a contiguous array called componentdataarray. The component data is stored pointwise with an additional header (metadata) stored for each point. The header has metadata information such as number of components at each point, number of variables for each component, offsets for the components data, etc.
1483 */
1484 PetscErrorCode DMNetworkComponentSetUp(DM dm)
1485 {
1486   DM_Network                        *network = (DM_Network *)dm->data;
1487   PetscInt                           arr_size, p, offset, offsetp, ncomp, i, *headerarr;
1488   DMNetworkComponentHeader           header;
1489   DMNetworkComponentValue            cvalue;
1490   DMNetworkComponentHeader           headerinfo;
1491   DMNetworkComponentGenericDataType *componentdataarray;
1492 
1493   PetscFunctionBegin;
1494   PetscCall(PetscSectionSetUp(network->DataSection));
1495   PetscCall(PetscSectionGetStorageSize(network->DataSection, &arr_size));
1496   /* arr_size+1 fixes pipeline test of opensolaris-misc for src/dm/tests/ex10.c -- Do not know why */
1497   PetscCall(PetscCalloc1(arr_size + 1, &network->componentdataarray));
1498   componentdataarray = network->componentdataarray;
1499   for (p = network->cloneshared->pStart; p < network->cloneshared->pEnd; p++) {
1500     PetscCall(PetscSectionGetOffset(network->DataSection, p, &offsetp));
1501     /* Copy header */
1502     header     = &network->header[p];
1503     headerinfo = (DMNetworkComponentHeader)(componentdataarray + offsetp);
1504     PetscCall(PetscMemcpy(headerinfo, header, sizeof(struct _p_DMNetworkComponentHeader)));
1505     headerarr = (PetscInt *)(headerinfo + 1);
1506     PetscCall(PetscMemcpy(headerarr, header->size, header->maxcomps * sizeof(PetscInt)));
1507     headerinfo->size = headerarr;
1508     headerarr += header->maxcomps;
1509     PetscCall(PetscMemcpy(headerarr, header->key, header->maxcomps * sizeof(PetscInt)));
1510     headerinfo->key = headerarr;
1511     headerarr += header->maxcomps;
1512     PetscCall(PetscMemcpy(headerarr, header->offset, header->maxcomps * sizeof(PetscInt)));
1513     headerinfo->offset = headerarr;
1514     headerarr += header->maxcomps;
1515     PetscCall(PetscMemcpy(headerarr, header->nvar, header->maxcomps * sizeof(PetscInt)));
1516     headerinfo->nvar = headerarr;
1517     headerarr += header->maxcomps;
1518     PetscCall(PetscMemcpy(headerarr, header->offsetvarrel, header->maxcomps * sizeof(PetscInt)));
1519     headerinfo->offsetvarrel = headerarr;
1520 
1521     /* Copy data */
1522     cvalue = &network->cvalue[p];
1523     ncomp  = header->ndata;
1524 
1525     for (i = 0; i < ncomp; i++) {
1526       offset = offsetp + header->hsize + header->offset[i];
1527       PetscCall(PetscMemcpy(componentdataarray + offset, cvalue->data[i], header->size[i] * sizeof(DMNetworkComponentGenericDataType)));
1528     }
1529   }
1530 
1531   for (i = network->cloneshared->pStart; i < network->cloneshared->pEnd; i++) {
1532     PetscCall(PetscFree5(network->header[i].size, network->header[i].key, network->header[i].offset, network->header[i].nvar, network->header[i].offsetvarrel));
1533     PetscCall(PetscFree(network->cvalue[i].data));
1534   }
1535   PetscCall(PetscFree2(network->header, network->cvalue));
1536   PetscFunctionReturn(0);
1537 }
1538 
1539 /* Sets up the section for dofs. This routine is called during DMSetUp() */
1540 static PetscErrorCode DMNetworkVariablesSetUp(DM dm)
1541 {
1542   DM_Network *network = (DM_Network *)dm->data;
1543 
1544   PetscFunctionBegin;
1545   PetscCall(PetscSectionSetUp(network->DofSection));
1546   PetscFunctionReturn(0);
1547 }
1548 
1549 /* Get a subsection from a range of points */
1550 static PetscErrorCode DMNetworkGetSubSection_private(PetscSection main, PetscInt pstart, PetscInt pend, PetscSection *subsection)
1551 {
1552   PetscInt i, nvar;
1553 
1554   PetscFunctionBegin;
1555   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)main), subsection));
1556   PetscCall(PetscSectionSetChart(*subsection, 0, pend - pstart));
1557   for (i = pstart; i < pend; i++) {
1558     PetscCall(PetscSectionGetDof(main, i, &nvar));
1559     PetscCall(PetscSectionSetDof(*subsection, i - pstart, nvar));
1560   }
1561 
1562   PetscCall(PetscSectionSetUp(*subsection));
1563   PetscFunctionReturn(0);
1564 }
1565 
1566 /* Create a submap of points with a GlobalToLocal structure */
1567 static PetscErrorCode DMNetworkSetSubMap_private(PetscInt pstart, PetscInt pend, ISLocalToGlobalMapping *map)
1568 {
1569   PetscInt i, *subpoints;
1570 
1571   PetscFunctionBegin;
1572   /* Create index sets to map from "points" to "subpoints" */
1573   PetscCall(PetscMalloc1(pend - pstart, &subpoints));
1574   for (i = pstart; i < pend; i++) subpoints[i - pstart] = i;
1575   PetscCall(ISLocalToGlobalMappingCreate(PETSC_COMM_WORLD, 1, pend - pstart, subpoints, PETSC_COPY_VALUES, map));
1576   PetscCall(PetscFree(subpoints));
1577   PetscFunctionReturn(0);
1578 }
1579 
1580 /*@
1581   DMNetworkAssembleGraphStructures - Assembles vertex and edge data structures. Must be called after DMNetworkDistribute
1582 
1583   Collective on dm
1584 
1585   Input Parameters:
1586 . dm - the DMNetworkObject
1587 
1588   Note: the routine will create alternative orderings for the vertices and edges. Assume global network points are:
1589 
1590   points = [0 1 2 3 4 5 6]
1591 
1592   where edges = [0,1,2,3] and vertices = [4,5,6]. The new orderings will be specific to the subset (i.e vertices = [0,1,2] <- [4,5,6]).
1593 
1594   With this new ordering a local PetscSection, global PetscSection and PetscSF will be created specific to the subset.
1595 
1596   Level: intermediate
1597 
1598 @*/
1599 PetscErrorCode DMNetworkAssembleGraphStructures(DM dm)
1600 {
1601   MPI_Comm    comm;
1602   PetscMPIInt size;
1603   DM_Network *network = (DM_Network *)dm->data;
1604 
1605   PetscFunctionBegin;
1606   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1607   PetscCallMPI(MPI_Comm_size(comm, &size));
1608 
1609   /* Create maps for vertices and edges */
1610   PetscCall(DMNetworkSetSubMap_private(network->cloneshared->vStart, network->cloneshared->vEnd, &network->vertex.mapping));
1611   PetscCall(DMNetworkSetSubMap_private(network->cloneshared->eStart, network->cloneshared->eEnd, &network->edge.mapping));
1612 
1613   /* Create local sub-sections */
1614   PetscCall(DMNetworkGetSubSection_private(network->DofSection, network->cloneshared->vStart, network->cloneshared->vEnd, &network->vertex.DofSection));
1615   PetscCall(DMNetworkGetSubSection_private(network->DofSection, network->cloneshared->eStart, network->cloneshared->eEnd, &network->edge.DofSection));
1616 
1617   if (size > 1) {
1618     PetscCall(PetscSFGetSubSF(network->plex->sf, network->vertex.mapping, &network->vertex.sf));
1619 
1620     PetscCall(PetscSectionCreateGlobalSection(network->vertex.DofSection, network->vertex.sf, PETSC_FALSE, PETSC_FALSE, &network->vertex.GlobalDofSection));
1621     PetscCall(PetscSFGetSubSF(network->plex->sf, network->edge.mapping, &network->edge.sf));
1622     PetscCall(PetscSectionCreateGlobalSection(network->edge.DofSection, network->edge.sf, PETSC_FALSE, PETSC_FALSE, &network->edge.GlobalDofSection));
1623   } else {
1624     /* create structures for vertex */
1625     PetscCall(PetscSectionClone(network->vertex.DofSection, &network->vertex.GlobalDofSection));
1626     /* create structures for edge */
1627     PetscCall(PetscSectionClone(network->edge.DofSection, &network->edge.GlobalDofSection));
1628   }
1629 
1630   /* Add viewers */
1631   PetscCall(PetscObjectSetName((PetscObject)network->edge.GlobalDofSection, "Global edge dof section"));
1632   PetscCall(PetscObjectSetName((PetscObject)network->vertex.GlobalDofSection, "Global vertex dof section"));
1633   PetscCall(PetscSectionViewFromOptions(network->edge.GlobalDofSection, NULL, "-edge_global_section_view"));
1634   PetscCall(PetscSectionViewFromOptions(network->vertex.GlobalDofSection, NULL, "-vertex_global_section_view"));
1635   PetscFunctionReturn(0);
1636 }
1637 
1638 /*
1639    Setup a lookup btable for the input v's owning subnetworks
1640    - add all owing subnetworks that connect to this v to the btable
1641      vertex_subnetid = supportingedge_subnetid
1642 */
1643 static inline PetscErrorCode SetSubnetIdLookupBT(DM dm, PetscInt v, PetscInt Nsubnet, PetscBT btable)
1644 {
1645   PetscInt                 e, nedges, offset;
1646   const PetscInt          *edges;
1647   DM_Network              *newDMnetwork = (DM_Network *)dm->data;
1648   DMNetworkComponentHeader header;
1649 
1650   PetscFunctionBegin;
1651   PetscCall(PetscBTMemzero(Nsubnet, btable));
1652   PetscCall(DMNetworkGetSupportingEdges(dm, v, &nedges, &edges));
1653   for (e = 0; e < nedges; e++) {
1654     PetscCall(PetscSectionGetOffset(newDMnetwork->DataSection, edges[e], &offset));
1655     header = (DMNetworkComponentHeader)(newDMnetwork->componentdataarray + offset);
1656     PetscCall(PetscBTSet(btable, header->subnetid));
1657   }
1658   PetscFunctionReturn(0);
1659 }
1660 
1661 /*
1662   DMNetworkDistributeCoordinates - Internal function to distribute the coordinate network and coordinates.
1663 
1664   Collective
1665 
1666   Input Parameters:
1667   + dm - The original DMNetwork object
1668   - migrationSF - The PetscSF discribing the migrgration from dm to dmnew
1669   - newDM - The new distributed dmnetwork object.
1670 */
1671 
1672 static PetscErrorCode DMNetworkDistributeCoordinates(DM dm, PetscSF migrationSF, DM newDM)
1673 {
1674   DM_Network              *newDMnetwork = (DM_Network *)((newDM)->data), *newCoordnetwork, *oldCoordnetwork;
1675   DM                       cdm, newcdm;
1676   PetscInt                 cdim, bs, p, pStart, pEnd, offset;
1677   Vec                      oldCoord, newCoord;
1678   DMNetworkComponentHeader header;
1679   const char              *name;
1680 
1681   PetscFunctionBegin;
1682   /* Distribute the coordinate network and coordinates */
1683   PetscCall(DMGetCoordinateDim(dm, &cdim));
1684   PetscCall(DMSetCoordinateDim(newDM, cdim));
1685 
1686   /* Migrate only if original network had coordinates */
1687   PetscCall(DMGetCoordinatesLocal(dm, &oldCoord));
1688   if (oldCoord) {
1689     PetscCall(DMGetCoordinateDM(dm, &cdm));
1690     PetscCall(DMGetCoordinateDM(newDM, &newcdm));
1691     newCoordnetwork = (DM_Network *)newcdm->data;
1692     oldCoordnetwork = (DM_Network *)cdm->data;
1693 
1694     PetscCall(VecCreate(PETSC_COMM_SELF, &newCoord));
1695     PetscCall(PetscObjectGetName((PetscObject)oldCoord, &name));
1696     PetscCall(PetscObjectSetName((PetscObject)newCoord, name));
1697     PetscCall(VecGetBlockSize(oldCoord, &bs));
1698     PetscCall(VecSetBlockSize(newCoord, bs));
1699 
1700     PetscCall(DMPlexDistributeField(newDMnetwork->plex, migrationSF, oldCoordnetwork->DofSection, oldCoord, newCoordnetwork->DofSection, newCoord));
1701     PetscCall(DMSetCoordinatesLocal(newDM, newCoord));
1702 
1703     PetscCall(VecDestroy(&newCoord));
1704     /* Migrate the components from the orignal coordinate network to the new coordinate network */
1705     PetscCall(DMPlexDistributeData(newDMnetwork->plex, migrationSF, oldCoordnetwork->DataSection, MPIU_INT, (void *)oldCoordnetwork->componentdataarray, newCoordnetwork->DataSection, (void **)&newCoordnetwork->componentdataarray));
1706     /* update the header pointers in the new coordinate network components */
1707     PetscCall(PetscSectionGetChart(newCoordnetwork->DataSection, &pStart, &pEnd));
1708     for (p = pStart; p < pEnd; p++) {
1709       PetscCall(PetscSectionGetOffset(newCoordnetwork->DataSection, p, &offset));
1710       header = (DMNetworkComponentHeader)(newCoordnetwork->componentdataarray + offset);
1711       /* Update pointers */
1712       header->size         = (PetscInt *)(header + 1);
1713       header->key          = header->size + header->maxcomps;
1714       header->offset       = header->key + header->maxcomps;
1715       header->nvar         = header->offset + header->maxcomps;
1716       header->offsetvarrel = header->nvar + header->maxcomps;
1717     }
1718 
1719     PetscCall(DMSetLocalSection(newCoordnetwork->plex, newCoordnetwork->DofSection));
1720     PetscCall(DMGetGlobalSection(newCoordnetwork->plex, &newCoordnetwork->GlobalDofSection));
1721     newCoordnetwork->componentsetup = PETSC_TRUE;
1722   }
1723   PetscFunctionReturn(0);
1724 }
1725 
1726 /*@
1727   DMNetworkDistribute - Distributes the network and moves associated component data
1728 
1729   Collective
1730 
1731   Input Parameters:
1732 + DM - the DMNetwork object
1733 - overlap - the overlap of partitions, 0 is the default
1734 
1735   Options Database Key:
1736 + -dmnetwork_view - Calls DMView() at the conclusion of DMSetUp()
1737 - -dmnetwork_view_distributed - Calls DMView() at the conclusion of DMNetworkDistribute()
1738 
1739   Notes:
1740   Distributes the network with <overlap>-overlapping partitioning of the edges.
1741 
1742   Level: intermediate
1743 
1744 .seealso: `DMNetworkCreate()`
1745 @*/
1746 PetscErrorCode DMNetworkDistribute(DM *dm, PetscInt overlap)
1747 {
1748   MPI_Comm                 comm;
1749   PetscMPIInt              size;
1750   DM_Network              *oldDMnetwork = (DM_Network *)((*dm)->data), *newDMnetwork;
1751   PetscSF                  pointsf      = NULL;
1752   DM                       newDM;
1753   PetscInt                 j, e, v, offset, *subnetvtx, *subnetedge, Nsubnet, gidx, svtx_idx, nv, net;
1754   PetscInt                *sv;
1755   PetscBT                  btable;
1756   PetscPartitioner         part;
1757   DMNetworkComponentHeader header;
1758 
1759   PetscFunctionBegin;
1760   PetscValidPointer(dm, 1);
1761   PetscValidHeaderSpecific(*dm, DM_CLASSID, 1);
1762   PetscCall(PetscObjectGetComm((PetscObject)*dm, &comm));
1763   PetscCallMPI(MPI_Comm_size(comm, &size));
1764   if (size == 1) PetscFunctionReturn(0);
1765 
1766   PetscCheck(!overlap, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "overlap %" PetscInt_FMT " != 0 is not supported yet", overlap);
1767 
1768   /* This routine moves the component data to the appropriate processors. It makes use of the DataSection and the componentdataarray to move the component data to appropriate processors and returns a new DataSection and new componentdataarray. */
1769   PetscCall(PetscLogEventBegin(DMNetwork_Distribute, dm, 0, 0, 0));
1770   PetscCall(DMNetworkCreate(PetscObjectComm((PetscObject)*dm), &newDM));
1771   newDMnetwork                       = (DM_Network *)newDM->data;
1772   newDMnetwork->max_comps_registered = oldDMnetwork->max_comps_registered;
1773   PetscCall(PetscMalloc1(newDMnetwork->max_comps_registered, &newDMnetwork->component));
1774 
1775   /* Enable runtime options for petscpartitioner */
1776   PetscCall(DMPlexGetPartitioner(oldDMnetwork->plex, &part));
1777   PetscCall(PetscPartitionerSetFromOptions(part));
1778 
1779   /* Distribute plex dm */
1780   PetscCall(DMPlexDistribute(oldDMnetwork->plex, overlap, &pointsf, &newDMnetwork->plex));
1781 
1782   /* Distribute dof section */
1783   PetscCall(PetscSectionCreate(comm, &newDMnetwork->DofSection));
1784   PetscCall(PetscSFDistributeSection(pointsf, oldDMnetwork->DofSection, NULL, newDMnetwork->DofSection));
1785 
1786   /* Distribute data and associated section */
1787   PetscCall(PetscSectionCreate(comm, &newDMnetwork->DataSection));
1788   PetscCall(DMPlexDistributeData(newDMnetwork->plex, pointsf, oldDMnetwork->DataSection, MPIU_INT, (void *)oldDMnetwork->componentdataarray, newDMnetwork->DataSection, (void **)&newDMnetwork->componentdataarray));
1789 
1790   PetscCall(PetscSectionGetChart(newDMnetwork->DataSection, &newDMnetwork->cloneshared->pStart, &newDMnetwork->cloneshared->pEnd));
1791   PetscCall(DMPlexGetHeightStratum(newDMnetwork->plex, 0, &newDMnetwork->cloneshared->eStart, &newDMnetwork->cloneshared->eEnd));
1792   PetscCall(DMPlexGetHeightStratum(newDMnetwork->plex, 1, &newDMnetwork->cloneshared->vStart, &newDMnetwork->cloneshared->vEnd));
1793   newDMnetwork->cloneshared->nEdges    = newDMnetwork->cloneshared->eEnd - newDMnetwork->cloneshared->eStart;
1794   newDMnetwork->cloneshared->nVertices = newDMnetwork->cloneshared->vEnd - newDMnetwork->cloneshared->vStart;
1795   newDMnetwork->cloneshared->NVertices = oldDMnetwork->cloneshared->NVertices;
1796   newDMnetwork->cloneshared->NEdges    = oldDMnetwork->cloneshared->NEdges;
1797   newDMnetwork->cloneshared->svtable   = oldDMnetwork->cloneshared->svtable; /* global table! */
1798   oldDMnetwork->cloneshared->svtable   = NULL;
1799 
1800   /* Set Dof section as the section for dm */
1801   PetscCall(DMSetLocalSection(newDMnetwork->plex, newDMnetwork->DofSection));
1802   PetscCall(DMGetGlobalSection(newDMnetwork->plex, &newDMnetwork->GlobalDofSection));
1803 
1804   /* Setup subnetwork info in the newDM */
1805   newDMnetwork->cloneshared->Nsubnet = oldDMnetwork->cloneshared->Nsubnet;
1806   newDMnetwork->cloneshared->Nsvtx   = oldDMnetwork->cloneshared->Nsvtx;
1807   oldDMnetwork->cloneshared->Nsvtx   = 0;
1808   newDMnetwork->cloneshared->svtx    = oldDMnetwork->cloneshared->svtx; /* global vertices! */
1809   oldDMnetwork->cloneshared->svtx    = NULL;
1810   PetscCall(PetscCalloc1(newDMnetwork->cloneshared->Nsubnet, &newDMnetwork->cloneshared->subnet));
1811 
1812   /* Copy over the global number of vertices and edges in each subnetwork.
1813      Note: these are calculated in DMNetworkLayoutSetUp()
1814   */
1815   Nsubnet = newDMnetwork->cloneshared->Nsubnet;
1816   for (j = 0; j < Nsubnet; j++) {
1817     newDMnetwork->cloneshared->subnet[j].Nvtx  = oldDMnetwork->cloneshared->subnet[j].Nvtx;
1818     newDMnetwork->cloneshared->subnet[j].Nedge = oldDMnetwork->cloneshared->subnet[j].Nedge;
1819   }
1820 
1821   /* Count local nedges for subnetworks */
1822   for (e = newDMnetwork->cloneshared->eStart; e < newDMnetwork->cloneshared->eEnd; e++) {
1823     PetscCall(PetscSectionGetOffset(newDMnetwork->DataSection, e, &offset));
1824     header = (DMNetworkComponentHeader)(newDMnetwork->componentdataarray + offset);
1825 
1826     /* Update pointers */
1827     header->size         = (PetscInt *)(header + 1);
1828     header->key          = header->size + header->maxcomps;
1829     header->offset       = header->key + header->maxcomps;
1830     header->nvar         = header->offset + header->maxcomps;
1831     header->offsetvarrel = header->nvar + header->maxcomps;
1832 
1833     newDMnetwork->cloneshared->subnet[header->subnetid].nedge++;
1834   }
1835 
1836   /* Setup a btable to keep track subnetworks owned by this process at a shared vertex */
1837   if (newDMnetwork->cloneshared->Nsvtx) PetscCall(PetscBTCreate(Nsubnet, &btable));
1838 
1839   /* Count local nvtx for subnetworks */
1840   for (v = newDMnetwork->cloneshared->vStart; v < newDMnetwork->cloneshared->vEnd; v++) {
1841     PetscCall(PetscSectionGetOffset(newDMnetwork->DataSection, v, &offset));
1842     header = (DMNetworkComponentHeader)(newDMnetwork->componentdataarray + offset);
1843 
1844     /* Update pointers */
1845     header->size         = (PetscInt *)(header + 1);
1846     header->key          = header->size + header->maxcomps;
1847     header->offset       = header->key + header->maxcomps;
1848     header->nvar         = header->offset + header->maxcomps;
1849     header->offsetvarrel = header->nvar + header->maxcomps;
1850 
1851     /* shared vertices: use gidx=header->index to check if v is a shared vertex */
1852     gidx = header->index;
1853     PetscCall(PetscTableFind(newDMnetwork->cloneshared->svtable, gidx + 1, &svtx_idx));
1854     svtx_idx--;
1855 
1856     if (svtx_idx < 0) { /* not a shared vertex */
1857       newDMnetwork->cloneshared->subnet[header->subnetid].nvtx++;
1858     } else { /* a shared vertex belongs to more than one subnetworks, it is being counted by multiple subnets */
1859       /* Setup a lookup btable for this v's owning subnetworks */
1860       PetscCall(SetSubnetIdLookupBT(newDM, v, Nsubnet, btable));
1861 
1862       for (j = 0; j < newDMnetwork->cloneshared->svtx[svtx_idx].n; j++) {
1863         sv  = newDMnetwork->cloneshared->svtx[svtx_idx].sv + 2 * j;
1864         net = sv[0];
1865         if (PetscBTLookup(btable, net)) newDMnetwork->cloneshared->subnet[net].nvtx++; /* sv is on net owned by this proces */
1866       }
1867     }
1868   }
1869 
1870   /* Get total local nvtx for subnetworks */
1871   nv = 0;
1872   for (j = 0; j < Nsubnet; j++) nv += newDMnetwork->cloneshared->subnet[j].nvtx;
1873   nv += newDMnetwork->cloneshared->Nsvtx;
1874 
1875   /* Now create the vertices and edge arrays for the subnetworks */
1876   PetscCall(PetscCalloc2(newDMnetwork->cloneshared->nEdges, &subnetedge, nv, &subnetvtx)); /* Maps local vertex to local subnetwork's vertex */
1877   newDMnetwork->cloneshared->subnetedge = subnetedge;
1878   newDMnetwork->cloneshared->subnetvtx  = subnetvtx;
1879   for (j = 0; j < newDMnetwork->cloneshared->Nsubnet; j++) {
1880     newDMnetwork->cloneshared->subnet[j].edges = subnetedge;
1881     subnetedge += newDMnetwork->cloneshared->subnet[j].nedge;
1882 
1883     newDMnetwork->cloneshared->subnet[j].vertices = subnetvtx;
1884     subnetvtx += newDMnetwork->cloneshared->subnet[j].nvtx;
1885 
1886     /* Temporarily setting nvtx and nedge to 0 so we can use them as counters in the below for loop. These get updated when the vertices and edges are added. */
1887     newDMnetwork->cloneshared->subnet[j].nvtx = newDMnetwork->cloneshared->subnet[j].nedge = 0;
1888   }
1889   newDMnetwork->cloneshared->svertices = subnetvtx;
1890 
1891   /* Set the edges and vertices in each subnetwork */
1892   for (e = newDMnetwork->cloneshared->eStart; e < newDMnetwork->cloneshared->eEnd; e++) {
1893     PetscCall(PetscSectionGetOffset(newDMnetwork->DataSection, e, &offset));
1894     header                                                                                                                 = (DMNetworkComponentHeader)(newDMnetwork->componentdataarray + offset);
1895     newDMnetwork->cloneshared->subnet[header->subnetid].edges[newDMnetwork->cloneshared->subnet[header->subnetid].nedge++] = e;
1896   }
1897 
1898   nv = 0;
1899   for (v = newDMnetwork->cloneshared->vStart; v < newDMnetwork->cloneshared->vEnd; v++) {
1900     PetscCall(PetscSectionGetOffset(newDMnetwork->DataSection, v, &offset));
1901     header = (DMNetworkComponentHeader)(newDMnetwork->componentdataarray + offset);
1902 
1903     /* coupling vertices: use gidx = header->index to check if v is a coupling vertex */
1904     PetscCall(PetscTableFind(newDMnetwork->cloneshared->svtable, header->index + 1, &svtx_idx));
1905     svtx_idx--;
1906     if (svtx_idx < 0) {
1907       newDMnetwork->cloneshared->subnet[header->subnetid].vertices[newDMnetwork->cloneshared->subnet[header->subnetid].nvtx++] = v;
1908     } else { /* a shared vertex */
1909       newDMnetwork->cloneshared->svertices[nv++] = v;
1910 
1911       /* Setup a lookup btable for this v's owning subnetworks */
1912       PetscCall(SetSubnetIdLookupBT(newDM, v, Nsubnet, btable));
1913 
1914       for (j = 0; j < newDMnetwork->cloneshared->svtx[svtx_idx].n; j++) {
1915         sv  = newDMnetwork->cloneshared->svtx[svtx_idx].sv + 2 * j;
1916         net = sv[0];
1917         if (PetscBTLookup(btable, net)) newDMnetwork->cloneshared->subnet[net].vertices[newDMnetwork->cloneshared->subnet[net].nvtx++] = v;
1918       }
1919     }
1920   }
1921   newDMnetwork->cloneshared->nsvtx = nv; /* num of local shared vertices */
1922 
1923   PetscCall(DMNetworkDistributeCoordinates(*dm, pointsf, newDM));
1924   newDM->setupcalled                          = (*dm)->setupcalled;
1925   newDMnetwork->cloneshared->distributecalled = PETSC_TRUE;
1926 
1927   /* Free spaces */
1928   PetscCall(PetscSFDestroy(&pointsf));
1929   PetscCall(DMDestroy(dm));
1930   if (newDMnetwork->cloneshared->Nsvtx) PetscCall(PetscBTDestroy(&btable));
1931 
1932   /* View distributed dmnetwork */
1933   PetscCall(DMViewFromOptions(newDM, NULL, "-dmnetwork_view_distributed"));
1934 
1935   *dm = newDM;
1936   PetscCall(PetscLogEventEnd(DMNetwork_Distribute, dm, 0, 0, 0));
1937   PetscFunctionReturn(0);
1938 }
1939 
1940 /*@C
1941   PetscSFGetSubSF - Returns an SF for a specific subset of points. Leaves are re-numbered to reflect the new ordering
1942 
1943  Collective
1944 
1945   Input Parameters:
1946 + mainSF - the original SF structure
1947 - map - a ISLocalToGlobal mapping that contains the subset of points
1948 
1949   Output Parameter:
1950 . subSF - a subset of the mainSF for the desired subset.
1951 
1952   Level: intermediate
1953 @*/
1954 PetscErrorCode PetscSFGetSubSF(PetscSF mainsf, ISLocalToGlobalMapping map, PetscSF *subSF)
1955 {
1956   PetscInt           nroots, nleaves, *ilocal_sub;
1957   PetscInt           i, *ilocal_map, nroots_sub, nleaves_sub = 0;
1958   PetscInt          *local_points, *remote_points;
1959   PetscSFNode       *iremote_sub;
1960   const PetscInt    *ilocal;
1961   const PetscSFNode *iremote;
1962 
1963   PetscFunctionBegin;
1964   PetscCall(PetscSFGetGraph(mainsf, &nroots, &nleaves, &ilocal, &iremote));
1965 
1966   /* Look for leaves that pertain to the subset of points. Get the local ordering */
1967   PetscCall(PetscMalloc1(nleaves, &ilocal_map));
1968   PetscCall(ISGlobalToLocalMappingApply(map, IS_GTOLM_MASK, nleaves, ilocal, NULL, ilocal_map));
1969   for (i = 0; i < nleaves; i++) {
1970     if (ilocal_map[i] != -1) nleaves_sub += 1;
1971   }
1972   /* Re-number ilocal with subset numbering. Need information from roots */
1973   PetscCall(PetscMalloc2(nroots, &local_points, nroots, &remote_points));
1974   for (i = 0; i < nroots; i++) local_points[i] = i;
1975   PetscCall(ISGlobalToLocalMappingApply(map, IS_GTOLM_MASK, nroots, local_points, NULL, local_points));
1976   PetscCall(PetscSFBcastBegin(mainsf, MPIU_INT, local_points, remote_points, MPI_REPLACE));
1977   PetscCall(PetscSFBcastEnd(mainsf, MPIU_INT, local_points, remote_points, MPI_REPLACE));
1978   /* Fill up graph using local (that is, local to the subset) numbering. */
1979   PetscCall(PetscMalloc1(nleaves_sub, &ilocal_sub));
1980   PetscCall(PetscMalloc1(nleaves_sub, &iremote_sub));
1981   nleaves_sub = 0;
1982   for (i = 0; i < nleaves; i++) {
1983     if (ilocal_map[i] != -1) {
1984       ilocal_sub[nleaves_sub]        = ilocal_map[i];
1985       iremote_sub[nleaves_sub].rank  = iremote[i].rank;
1986       iremote_sub[nleaves_sub].index = remote_points[ilocal[i]];
1987       nleaves_sub += 1;
1988     }
1989   }
1990   PetscCall(PetscFree2(local_points, remote_points));
1991   PetscCall(ISLocalToGlobalMappingGetSize(map, &nroots_sub));
1992 
1993   /* Create new subSF */
1994   PetscCall(PetscSFCreate(PETSC_COMM_WORLD, subSF));
1995   PetscCall(PetscSFSetFromOptions(*subSF));
1996   PetscCall(PetscSFSetGraph(*subSF, nroots_sub, nleaves_sub, ilocal_sub, PETSC_OWN_POINTER, iremote_sub, PETSC_COPY_VALUES));
1997   PetscCall(PetscFree(ilocal_map));
1998   PetscCall(PetscFree(iremote_sub));
1999   PetscFunctionReturn(0);
2000 }
2001 
2002 /*@C
2003   DMNetworkGetSupportingEdges - Return the supporting edges for this vertex point
2004 
2005   Not Collective
2006 
2007   Input Parameters:
2008 + dm - the DMNetwork object
2009 - p  - the vertex point
2010 
2011   Output Parameters:
2012 + nedges - number of edges connected to this vertex point
2013 - edges  - list of edge points
2014 
2015   Level: beginner
2016 
2017   Fortran Notes:
2018   Since it returns an array, this routine is only available in Fortran 90, and you must
2019   include petsc.h90 in your code.
2020 
2021 .seealso: `DMNetworkCreate()`, `DMNetworkGetConnectedVertices()`
2022 @*/
2023 PetscErrorCode DMNetworkGetSupportingEdges(DM dm, PetscInt vertex, PetscInt *nedges, const PetscInt *edges[])
2024 {
2025   DM_Network *network = (DM_Network *)dm->data;
2026 
2027   PetscFunctionBegin;
2028   PetscCall(DMPlexGetSupportSize(network->plex, vertex, nedges));
2029   if (edges) PetscCall(DMPlexGetSupport(network->plex, vertex, edges));
2030   PetscFunctionReturn(0);
2031 }
2032 
2033 /*@C
2034   DMNetworkGetConnectedVertices - Return the connected vertices for this edge point
2035 
2036   Not Collective
2037 
2038   Input Parameters:
2039 + dm - the DMNetwork object
2040 - p - the edge point
2041 
2042   Output Parameters:
2043 . vertices - vertices connected to this edge
2044 
2045   Level: beginner
2046 
2047   Fortran Notes:
2048   Since it returns an array, this routine is only available in Fortran 90, and you must
2049   include petsc.h90 in your code.
2050 
2051 .seealso: `DMNetworkCreate()`, `DMNetworkGetSupportingEdges()`
2052 @*/
2053 PetscErrorCode DMNetworkGetConnectedVertices(DM dm, PetscInt edge, const PetscInt *vertices[])
2054 {
2055   DM_Network *network = (DM_Network *)dm->data;
2056 
2057   PetscFunctionBegin;
2058   PetscCall(DMPlexGetCone(network->plex, edge, vertices));
2059   PetscFunctionReturn(0);
2060 }
2061 
2062 /*@
2063   DMNetworkIsSharedVertex - Returns TRUE if the vertex is shared by subnetworks
2064 
2065   Not Collective
2066 
2067   Input Parameters:
2068 + dm - the DMNetwork object
2069 - p - the vertex point
2070 
2071   Output Parameter:
2072 . flag - TRUE if the vertex is shared by subnetworks
2073 
2074   Level: beginner
2075 
2076 .seealso: `DMNetworkAddSharedVertices()`, `DMNetworkIsGhostVertex()`
2077 @*/
2078 PetscErrorCode DMNetworkIsSharedVertex(DM dm, PetscInt p, PetscBool *flag)
2079 {
2080   PetscInt i;
2081 
2082   PetscFunctionBegin;
2083   *flag = PETSC_FALSE;
2084 
2085   if (dm->setupcalled) { /* DMNetworkGetGlobalVertexIndex() requires DMSetUp() be called */
2086     DM_Network *network = (DM_Network *)dm->data;
2087     PetscInt    gidx;
2088     PetscCall(DMNetworkGetGlobalVertexIndex(dm, p, &gidx));
2089     PetscCall(PetscTableFind(network->cloneshared->svtable, gidx + 1, &i));
2090     if (i) *flag = PETSC_TRUE;
2091   } else { /* would be removed? */
2092     PetscInt        nv;
2093     const PetscInt *vtx;
2094     PetscCall(DMNetworkGetSharedVertices(dm, &nv, &vtx));
2095     for (i = 0; i < nv; i++) {
2096       if (p == vtx[i]) {
2097         *flag = PETSC_TRUE;
2098         break;
2099       }
2100     }
2101   }
2102   PetscFunctionReturn(0);
2103 }
2104 
2105 /*@
2106   DMNetworkIsGhostVertex - Returns TRUE if the vertex is a ghost vertex
2107 
2108   Not Collective
2109 
2110   Input Parameters:
2111 + dm - the DMNetwork object
2112 - p - the vertex point
2113 
2114   Output Parameter:
2115 . isghost - TRUE if the vertex is a ghost point
2116 
2117   Level: beginner
2118 
2119 .seealso: `DMNetworkGetConnectedVertices()`, `DMNetworkGetVertexRange()`, `DMNetworkIsSharedVertex()`
2120 @*/
2121 PetscErrorCode DMNetworkIsGhostVertex(DM dm, PetscInt p, PetscBool *isghost)
2122 {
2123   DM_Network  *network = (DM_Network *)dm->data;
2124   PetscInt     offsetg;
2125   PetscSection sectiong;
2126 
2127   PetscFunctionBegin;
2128   *isghost = PETSC_FALSE;
2129   PetscCall(DMGetGlobalSection(network->plex, &sectiong));
2130   PetscCall(PetscSectionGetOffset(sectiong, p, &offsetg));
2131   if (offsetg < 0) *isghost = PETSC_TRUE;
2132   PetscFunctionReturn(0);
2133 }
2134 
2135 PetscErrorCode DMSetUp_Network(DM dm)
2136 {
2137   PetscFunctionBegin;
2138   PetscCall(PetscLogEventBegin(DMNetwork_SetUpNetwork, dm, 0, 0, 0));
2139   PetscCall(DMNetworkFinalizeComponents(dm));
2140   /* View dmnetwork */
2141   PetscCall(DMViewFromOptions(dm, NULL, "-dmnetwork_view"));
2142   PetscCall(PetscLogEventEnd(DMNetwork_SetUpNetwork, dm, 0, 0, 0));
2143   PetscFunctionReturn(0);
2144 }
2145 
2146 /*@
2147   DMNetworkHasJacobian - Sets global flag for using user's sub Jacobian matrices
2148       -- replaced by DMNetworkSetOption(network,userjacobian,PETSC_TURE)?
2149 
2150   Collective
2151 
2152   Input Parameters:
2153 + dm - the DMNetwork object
2154 . eflg - turn the option on (PETSC_TRUE) or off (PETSC_FALSE) if user provides Jacobian for edges
2155 - vflg - turn the option on (PETSC_TRUE) or off (PETSC_FALSE) if user provides Jacobian for vertices
2156 
2157  Level: intermediate
2158 
2159 @*/
2160 PetscErrorCode DMNetworkHasJacobian(DM dm, PetscBool eflg, PetscBool vflg)
2161 {
2162   DM_Network *network   = (DM_Network *)dm->data;
2163   PetscInt    nVertices = network->cloneshared->nVertices;
2164 
2165   PetscFunctionBegin;
2166   network->userEdgeJacobian   = eflg;
2167   network->userVertexJacobian = vflg;
2168 
2169   if (eflg && !network->Je) PetscCall(PetscCalloc1(3 * network->cloneshared->nEdges, &network->Je));
2170 
2171   if (vflg && !network->Jv && nVertices) {
2172     PetscInt        i, *vptr, nedges, vStart = network->cloneshared->vStart;
2173     PetscInt        nedges_total;
2174     const PetscInt *edges;
2175 
2176     /* count nvertex_total */
2177     nedges_total = 0;
2178     PetscCall(PetscMalloc1(nVertices + 1, &vptr));
2179 
2180     vptr[0] = 0;
2181     for (i = 0; i < nVertices; i++) {
2182       PetscCall(DMNetworkGetSupportingEdges(dm, i + vStart, &nedges, &edges));
2183       nedges_total += nedges;
2184       vptr[i + 1] = vptr[i] + 2 * nedges + 1;
2185     }
2186 
2187     PetscCall(PetscCalloc1(2 * nedges_total + nVertices, &network->Jv));
2188     network->Jvptr = vptr;
2189   }
2190   PetscFunctionReturn(0);
2191 }
2192 
2193 /*@
2194   DMNetworkEdgeSetMatrix - Sets user-provided Jacobian matrices for this edge to the network
2195 
2196   Not Collective
2197 
2198   Input Parameters:
2199 + dm - the DMNetwork object
2200 . p - the edge point
2201 - J - array (size = 3) of Jacobian submatrices for this edge point:
2202         J[0]: this edge
2203         J[1] and J[2]: connected vertices, obtained by calling DMNetworkGetConnectedVertices()
2204 
2205   Level: advanced
2206 
2207 .seealso: `DMNetworkVertexSetMatrix()`
2208 @*/
2209 PetscErrorCode DMNetworkEdgeSetMatrix(DM dm, PetscInt p, Mat J[])
2210 {
2211   DM_Network *network = (DM_Network *)dm->data;
2212 
2213   PetscFunctionBegin;
2214   PetscCheck(network->Je, PetscObjectComm((PetscObject)dm), PETSC_ERR_ORDER, "Must call DMNetworkHasJacobian() collectively before calling DMNetworkEdgeSetMatrix");
2215 
2216   if (J) {
2217     network->Je[3 * p]     = J[0];
2218     network->Je[3 * p + 1] = J[1];
2219     network->Je[3 * p + 2] = J[2];
2220   }
2221   PetscFunctionReturn(0);
2222 }
2223 
2224 /*@
2225   DMNetworkVertexSetMatrix - Sets user-provided Jacobian matrix for this vertex to the network
2226 
2227   Not Collective
2228 
2229   Input Parameters:
2230 + dm - The DMNetwork object
2231 . p - the vertex point
2232 - J - array of Jacobian (size = 2*(num of supporting edges) + 1) submatrices for this vertex point:
2233         J[0]:       this vertex
2234         J[1+2*i]:   i-th supporting edge
2235         J[1+2*i+1]: i-th connected vertex
2236 
2237   Level: advanced
2238 
2239 .seealso: `DMNetworkEdgeSetMatrix()`
2240 @*/
2241 PetscErrorCode DMNetworkVertexSetMatrix(DM dm, PetscInt p, Mat J[])
2242 {
2243   DM_Network     *network = (DM_Network *)dm->data;
2244   PetscInt        i, *vptr, nedges, vStart = network->cloneshared->vStart;
2245   const PetscInt *edges;
2246 
2247   PetscFunctionBegin;
2248   PetscCheck(network->Jv, PetscObjectComm((PetscObject)dm), PETSC_ERR_ORDER, "Must call DMNetworkHasJacobian() collectively before calling DMNetworkVertexSetMatrix");
2249 
2250   if (J) {
2251     vptr                          = network->Jvptr;
2252     network->Jv[vptr[p - vStart]] = J[0]; /* Set Jacobian for this vertex */
2253 
2254     /* Set Jacobian for each supporting edge and connected vertex */
2255     PetscCall(DMNetworkGetSupportingEdges(dm, p, &nedges, &edges));
2256     for (i = 1; i <= 2 * nedges; i++) network->Jv[vptr[p - vStart] + i] = J[i];
2257   }
2258   PetscFunctionReturn(0);
2259 }
2260 
2261 static inline PetscErrorCode MatSetPreallocationDenseblock_private(PetscInt nrows, PetscInt *rows, PetscInt ncols, PetscBool ghost, Vec vdnz, Vec vonz)
2262 {
2263   PetscInt    j;
2264   PetscScalar val = (PetscScalar)ncols;
2265 
2266   PetscFunctionBegin;
2267   if (!ghost) {
2268     for (j = 0; j < nrows; j++) PetscCall(VecSetValues(vdnz, 1, &rows[j], &val, ADD_VALUES));
2269   } else {
2270     for (j = 0; j < nrows; j++) PetscCall(VecSetValues(vonz, 1, &rows[j], &val, ADD_VALUES));
2271   }
2272   PetscFunctionReturn(0);
2273 }
2274 
2275 static inline PetscErrorCode MatSetPreallocationUserblock_private(Mat Ju, PetscInt nrows, PetscInt *rows, PetscInt ncols, PetscBool ghost, Vec vdnz, Vec vonz)
2276 {
2277   PetscInt    j, ncols_u;
2278   PetscScalar val;
2279 
2280   PetscFunctionBegin;
2281   if (!ghost) {
2282     for (j = 0; j < nrows; j++) {
2283       PetscCall(MatGetRow(Ju, j, &ncols_u, NULL, NULL));
2284       val = (PetscScalar)ncols_u;
2285       PetscCall(VecSetValues(vdnz, 1, &rows[j], &val, ADD_VALUES));
2286       PetscCall(MatRestoreRow(Ju, j, &ncols_u, NULL, NULL));
2287     }
2288   } else {
2289     for (j = 0; j < nrows; j++) {
2290       PetscCall(MatGetRow(Ju, j, &ncols_u, NULL, NULL));
2291       val = (PetscScalar)ncols_u;
2292       PetscCall(VecSetValues(vonz, 1, &rows[j], &val, ADD_VALUES));
2293       PetscCall(MatRestoreRow(Ju, j, &ncols_u, NULL, NULL));
2294     }
2295   }
2296   PetscFunctionReturn(0);
2297 }
2298 
2299 static inline PetscErrorCode MatSetPreallocationblock_private(Mat Ju, PetscInt nrows, PetscInt *rows, PetscInt ncols, PetscBool ghost, Vec vdnz, Vec vonz)
2300 {
2301   PetscFunctionBegin;
2302   if (Ju) {
2303     PetscCall(MatSetPreallocationUserblock_private(Ju, nrows, rows, ncols, ghost, vdnz, vonz));
2304   } else {
2305     PetscCall(MatSetPreallocationDenseblock_private(nrows, rows, ncols, ghost, vdnz, vonz));
2306   }
2307   PetscFunctionReturn(0);
2308 }
2309 
2310 static inline PetscErrorCode MatSetDenseblock_private(PetscInt nrows, PetscInt *rows, PetscInt ncols, PetscInt cstart, Mat *J)
2311 {
2312   PetscInt     j, *cols;
2313   PetscScalar *zeros;
2314 
2315   PetscFunctionBegin;
2316   PetscCall(PetscCalloc2(ncols, &cols, nrows * ncols, &zeros));
2317   for (j = 0; j < ncols; j++) cols[j] = j + cstart;
2318   PetscCall(MatSetValues(*J, nrows, rows, ncols, cols, zeros, INSERT_VALUES));
2319   PetscCall(PetscFree2(cols, zeros));
2320   PetscFunctionReturn(0);
2321 }
2322 
2323 static inline PetscErrorCode MatSetUserblock_private(Mat Ju, PetscInt nrows, PetscInt *rows, PetscInt ncols, PetscInt cstart, Mat *J)
2324 {
2325   PetscInt        j, M, N, row, col, ncols_u;
2326   const PetscInt *cols;
2327   PetscScalar     zero = 0.0;
2328 
2329   PetscFunctionBegin;
2330   PetscCall(MatGetSize(Ju, &M, &N));
2331   PetscCheck(nrows == M && ncols == N, PetscObjectComm((PetscObject)Ju), PETSC_ERR_USER, "%" PetscInt_FMT " by %" PetscInt_FMT " must equal %" PetscInt_FMT " by %" PetscInt_FMT, nrows, ncols, M, N);
2332 
2333   for (row = 0; row < nrows; row++) {
2334     PetscCall(MatGetRow(Ju, row, &ncols_u, &cols, NULL));
2335     for (j = 0; j < ncols_u; j++) {
2336       col = cols[j] + cstart;
2337       PetscCall(MatSetValues(*J, 1, &rows[row], 1, &col, &zero, INSERT_VALUES));
2338     }
2339     PetscCall(MatRestoreRow(Ju, row, &ncols_u, &cols, NULL));
2340   }
2341   PetscFunctionReturn(0);
2342 }
2343 
2344 static inline PetscErrorCode MatSetblock_private(Mat Ju, PetscInt nrows, PetscInt *rows, PetscInt ncols, PetscInt cstart, Mat *J)
2345 {
2346   PetscFunctionBegin;
2347   if (Ju) {
2348     PetscCall(MatSetUserblock_private(Ju, nrows, rows, ncols, cstart, J));
2349   } else {
2350     PetscCall(MatSetDenseblock_private(nrows, rows, ncols, cstart, J));
2351   }
2352   PetscFunctionReturn(0);
2353 }
2354 
2355 /* Creates a GlobalToLocal mapping with a Local and Global section. This is akin to the routine DMGetLocalToGlobalMapping but without the need of providing a dm.
2356 */
2357 PetscErrorCode CreateSubGlobalToLocalMapping_private(PetscSection globalsec, PetscSection localsec, ISLocalToGlobalMapping *ltog)
2358 {
2359   PetscInt  i, size, dof;
2360   PetscInt *glob2loc;
2361 
2362   PetscFunctionBegin;
2363   PetscCall(PetscSectionGetStorageSize(localsec, &size));
2364   PetscCall(PetscMalloc1(size, &glob2loc));
2365 
2366   for (i = 0; i < size; i++) {
2367     PetscCall(PetscSectionGetOffset(globalsec, i, &dof));
2368     dof         = (dof >= 0) ? dof : -(dof + 1);
2369     glob2loc[i] = dof;
2370   }
2371 
2372   PetscCall(ISLocalToGlobalMappingCreate(PETSC_COMM_WORLD, 1, size, glob2loc, PETSC_OWN_POINTER, ltog));
2373 #if 0
2374   PetscCall(PetscIntView(size,glob2loc,PETSC_VIEWER_STDOUT_WORLD));
2375 #endif
2376   PetscFunctionReturn(0);
2377 }
2378 
2379 #include <petsc/private/matimpl.h>
2380 
2381 PetscErrorCode DMCreateMatrix_Network_Nest(DM dm, Mat *J)
2382 {
2383   DM_Network            *network = (DM_Network *)dm->data;
2384   PetscInt               eDof, vDof;
2385   Mat                    j11, j12, j21, j22, bA[2][2];
2386   MPI_Comm               comm;
2387   ISLocalToGlobalMapping eISMap, vISMap;
2388 
2389   PetscFunctionBegin;
2390   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
2391 
2392   PetscCall(PetscSectionGetConstrainedStorageSize(network->edge.GlobalDofSection, &eDof));
2393   PetscCall(PetscSectionGetConstrainedStorageSize(network->vertex.GlobalDofSection, &vDof));
2394 
2395   PetscCall(MatCreate(comm, &j11));
2396   PetscCall(MatSetSizes(j11, eDof, eDof, PETSC_DETERMINE, PETSC_DETERMINE));
2397   PetscCall(MatSetType(j11, MATMPIAIJ));
2398 
2399   PetscCall(MatCreate(comm, &j12));
2400   PetscCall(MatSetSizes(j12, eDof, vDof, PETSC_DETERMINE, PETSC_DETERMINE));
2401   PetscCall(MatSetType(j12, MATMPIAIJ));
2402 
2403   PetscCall(MatCreate(comm, &j21));
2404   PetscCall(MatSetSizes(j21, vDof, eDof, PETSC_DETERMINE, PETSC_DETERMINE));
2405   PetscCall(MatSetType(j21, MATMPIAIJ));
2406 
2407   PetscCall(MatCreate(comm, &j22));
2408   PetscCall(MatSetSizes(j22, vDof, vDof, PETSC_DETERMINE, PETSC_DETERMINE));
2409   PetscCall(MatSetType(j22, MATMPIAIJ));
2410 
2411   bA[0][0] = j11;
2412   bA[0][1] = j12;
2413   bA[1][0] = j21;
2414   bA[1][1] = j22;
2415 
2416   PetscCall(CreateSubGlobalToLocalMapping_private(network->edge.GlobalDofSection, network->edge.DofSection, &eISMap));
2417   PetscCall(CreateSubGlobalToLocalMapping_private(network->vertex.GlobalDofSection, network->vertex.DofSection, &vISMap));
2418 
2419   PetscCall(MatSetLocalToGlobalMapping(j11, eISMap, eISMap));
2420   PetscCall(MatSetLocalToGlobalMapping(j12, eISMap, vISMap));
2421   PetscCall(MatSetLocalToGlobalMapping(j21, vISMap, eISMap));
2422   PetscCall(MatSetLocalToGlobalMapping(j22, vISMap, vISMap));
2423 
2424   PetscCall(MatSetUp(j11));
2425   PetscCall(MatSetUp(j12));
2426   PetscCall(MatSetUp(j21));
2427   PetscCall(MatSetUp(j22));
2428 
2429   PetscCall(MatCreateNest(comm, 2, NULL, 2, NULL, &bA[0][0], J));
2430   PetscCall(MatSetUp(*J));
2431   PetscCall(MatNestSetVecType(*J, VECNEST));
2432   PetscCall(MatDestroy(&j11));
2433   PetscCall(MatDestroy(&j12));
2434   PetscCall(MatDestroy(&j21));
2435   PetscCall(MatDestroy(&j22));
2436 
2437   PetscCall(MatAssemblyBegin(*J, MAT_FINAL_ASSEMBLY));
2438   PetscCall(MatAssemblyEnd(*J, MAT_FINAL_ASSEMBLY));
2439   PetscCall(MatSetOption(*J, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE));
2440 
2441   /* Free structures */
2442   PetscCall(ISLocalToGlobalMappingDestroy(&eISMap));
2443   PetscCall(ISLocalToGlobalMappingDestroy(&vISMap));
2444   PetscFunctionReturn(0);
2445 }
2446 
2447 PetscErrorCode DMCreateMatrix_Network(DM dm, Mat *J)
2448 {
2449   DM_Network     *network = (DM_Network *)dm->data;
2450   PetscInt        eStart, eEnd, vStart, vEnd, rstart, nrows, *rows, localSize;
2451   PetscInt        cstart, ncols, j, e, v;
2452   PetscBool       ghost, ghost_vc, ghost2, isNest;
2453   Mat             Juser;
2454   PetscSection    sectionGlobal;
2455   PetscInt        nedges, *vptr = NULL, vc, *rows_v; /* suppress maybe-uninitialized warning */
2456   const PetscInt *edges, *cone;
2457   MPI_Comm        comm;
2458   MatType         mtype;
2459   Vec             vd_nz, vo_nz;
2460   PetscInt       *dnnz, *onnz;
2461   PetscScalar    *vdnz, *vonz;
2462 
2463   PetscFunctionBegin;
2464   mtype = dm->mattype;
2465   PetscCall(PetscStrcmp(mtype, MATNEST, &isNest));
2466   if (isNest) {
2467     PetscCall(DMCreateMatrix_Network_Nest(dm, J));
2468     PetscCall(MatSetDM(*J, dm));
2469     PetscFunctionReturn(0);
2470   }
2471 
2472   if (!network->userEdgeJacobian && !network->userVertexJacobian) {
2473     /* user does not provide Jacobian blocks */
2474     PetscCall(DMCreateMatrix_Plex(network->plex, J));
2475     PetscCall(MatSetDM(*J, dm));
2476     PetscFunctionReturn(0);
2477   }
2478 
2479   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2480   PetscCall(DMGetGlobalSection(network->plex, &sectionGlobal));
2481   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2482   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2483 
2484   PetscCall(MatSetType(*J, MATAIJ));
2485   PetscCall(MatSetFromOptions(*J));
2486 
2487   /* (1) Set matrix preallocation */
2488   /*------------------------------*/
2489   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
2490   PetscCall(VecCreate(comm, &vd_nz));
2491   PetscCall(VecSetSizes(vd_nz, localSize, PETSC_DECIDE));
2492   PetscCall(VecSetFromOptions(vd_nz));
2493   PetscCall(VecSet(vd_nz, 0.0));
2494   PetscCall(VecDuplicate(vd_nz, &vo_nz));
2495 
2496   /* Set preallocation for edges */
2497   /*-----------------------------*/
2498   PetscCall(DMNetworkGetEdgeRange(dm, &eStart, &eEnd));
2499 
2500   PetscCall(PetscMalloc1(localSize, &rows));
2501   for (e = eStart; e < eEnd; e++) {
2502     /* Get row indices */
2503     PetscCall(DMNetworkGetGlobalVecOffset(dm, e, ALL_COMPONENTS, &rstart));
2504     PetscCall(PetscSectionGetDof(network->DofSection, e, &nrows));
2505     if (nrows) {
2506       for (j = 0; j < nrows; j++) rows[j] = j + rstart;
2507 
2508       /* Set preallocation for connected vertices */
2509       PetscCall(DMNetworkGetConnectedVertices(dm, e, &cone));
2510       for (v = 0; v < 2; v++) {
2511         PetscCall(PetscSectionGetDof(network->DofSection, cone[v], &ncols));
2512 
2513         if (network->Je) {
2514           Juser = network->Je[3 * e + 1 + v]; /* Jacobian(e,v) */
2515         } else Juser = NULL;
2516         PetscCall(DMNetworkIsGhostVertex(dm, cone[v], &ghost));
2517         PetscCall(MatSetPreallocationblock_private(Juser, nrows, rows, ncols, ghost, vd_nz, vo_nz));
2518       }
2519 
2520       /* Set preallocation for edge self */
2521       cstart = rstart;
2522       if (network->Je) {
2523         Juser = network->Je[3 * e]; /* Jacobian(e,e) */
2524       } else Juser = NULL;
2525       PetscCall(MatSetPreallocationblock_private(Juser, nrows, rows, nrows, PETSC_FALSE, vd_nz, vo_nz));
2526     }
2527   }
2528 
2529   /* Set preallocation for vertices */
2530   /*--------------------------------*/
2531   PetscCall(DMNetworkGetVertexRange(dm, &vStart, &vEnd));
2532   if (vEnd - vStart) vptr = network->Jvptr;
2533 
2534   for (v = vStart; v < vEnd; v++) {
2535     /* Get row indices */
2536     PetscCall(DMNetworkGetGlobalVecOffset(dm, v, ALL_COMPONENTS, &rstart));
2537     PetscCall(PetscSectionGetDof(network->DofSection, v, &nrows));
2538     if (!nrows) continue;
2539 
2540     PetscCall(DMNetworkIsGhostVertex(dm, v, &ghost));
2541     if (ghost) {
2542       PetscCall(PetscMalloc1(nrows, &rows_v));
2543     } else {
2544       rows_v = rows;
2545     }
2546 
2547     for (j = 0; j < nrows; j++) rows_v[j] = j + rstart;
2548 
2549     /* Get supporting edges and connected vertices */
2550     PetscCall(DMNetworkGetSupportingEdges(dm, v, &nedges, &edges));
2551 
2552     for (e = 0; e < nedges; e++) {
2553       /* Supporting edges */
2554       PetscCall(DMNetworkGetGlobalVecOffset(dm, edges[e], ALL_COMPONENTS, &cstart));
2555       PetscCall(PetscSectionGetDof(network->DofSection, edges[e], &ncols));
2556 
2557       if (network->Jv) {
2558         Juser = network->Jv[vptr[v - vStart] + 2 * e + 1]; /* Jacobian(v,e) */
2559       } else Juser = NULL;
2560       PetscCall(MatSetPreallocationblock_private(Juser, nrows, rows_v, ncols, ghost, vd_nz, vo_nz));
2561 
2562       /* Connected vertices */
2563       PetscCall(DMNetworkGetConnectedVertices(dm, edges[e], &cone));
2564       vc = (v == cone[0]) ? cone[1] : cone[0];
2565       PetscCall(DMNetworkIsGhostVertex(dm, vc, &ghost_vc));
2566 
2567       PetscCall(PetscSectionGetDof(network->DofSection, vc, &ncols));
2568 
2569       if (network->Jv) {
2570         Juser = network->Jv[vptr[v - vStart] + 2 * e + 2]; /* Jacobian(v,vc) */
2571       } else Juser = NULL;
2572       if (ghost_vc || ghost) {
2573         ghost2 = PETSC_TRUE;
2574       } else {
2575         ghost2 = PETSC_FALSE;
2576       }
2577       PetscCall(MatSetPreallocationblock_private(Juser, nrows, rows_v, ncols, ghost2, vd_nz, vo_nz));
2578     }
2579 
2580     /* Set preallocation for vertex self */
2581     PetscCall(DMNetworkIsGhostVertex(dm, v, &ghost));
2582     if (!ghost) {
2583       PetscCall(DMNetworkGetGlobalVecOffset(dm, v, ALL_COMPONENTS, &cstart));
2584       if (network->Jv) {
2585         Juser = network->Jv[vptr[v - vStart]]; /* Jacobian(v,v) */
2586       } else Juser = NULL;
2587       PetscCall(MatSetPreallocationblock_private(Juser, nrows, rows_v, nrows, PETSC_FALSE, vd_nz, vo_nz));
2588     }
2589     if (ghost) PetscCall(PetscFree(rows_v));
2590   }
2591 
2592   PetscCall(VecAssemblyBegin(vd_nz));
2593   PetscCall(VecAssemblyBegin(vo_nz));
2594 
2595   PetscCall(PetscMalloc2(localSize, &dnnz, localSize, &onnz));
2596 
2597   PetscCall(VecAssemblyEnd(vd_nz));
2598   PetscCall(VecAssemblyEnd(vo_nz));
2599 
2600   PetscCall(VecGetArray(vd_nz, &vdnz));
2601   PetscCall(VecGetArray(vo_nz, &vonz));
2602   for (j = 0; j < localSize; j++) {
2603     dnnz[j] = (PetscInt)PetscRealPart(vdnz[j]);
2604     onnz[j] = (PetscInt)PetscRealPart(vonz[j]);
2605   }
2606   PetscCall(VecRestoreArray(vd_nz, &vdnz));
2607   PetscCall(VecRestoreArray(vo_nz, &vonz));
2608   PetscCall(VecDestroy(&vd_nz));
2609   PetscCall(VecDestroy(&vo_nz));
2610 
2611   PetscCall(MatSeqAIJSetPreallocation(*J, 0, dnnz));
2612   PetscCall(MatMPIAIJSetPreallocation(*J, 0, dnnz, 0, onnz));
2613   PetscCall(MatSetOption(*J, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE));
2614 
2615   PetscCall(PetscFree2(dnnz, onnz));
2616 
2617   /* (2) Set matrix entries for edges */
2618   /*----------------------------------*/
2619   for (e = eStart; e < eEnd; e++) {
2620     /* Get row indices */
2621     PetscCall(DMNetworkGetGlobalVecOffset(dm, e, ALL_COMPONENTS, &rstart));
2622     PetscCall(PetscSectionGetDof(network->DofSection, e, &nrows));
2623     if (nrows) {
2624       for (j = 0; j < nrows; j++) rows[j] = j + rstart;
2625 
2626       /* Set matrix entries for connected vertices */
2627       PetscCall(DMNetworkGetConnectedVertices(dm, e, &cone));
2628       for (v = 0; v < 2; v++) {
2629         PetscCall(DMNetworkGetGlobalVecOffset(dm, cone[v], ALL_COMPONENTS, &cstart));
2630         PetscCall(PetscSectionGetDof(network->DofSection, cone[v], &ncols));
2631 
2632         if (network->Je) {
2633           Juser = network->Je[3 * e + 1 + v]; /* Jacobian(e,v) */
2634         } else Juser = NULL;
2635         PetscCall(MatSetblock_private(Juser, nrows, rows, ncols, cstart, J));
2636       }
2637 
2638       /* Set matrix entries for edge self */
2639       cstart = rstart;
2640       if (network->Je) {
2641         Juser = network->Je[3 * e]; /* Jacobian(e,e) */
2642       } else Juser = NULL;
2643       PetscCall(MatSetblock_private(Juser, nrows, rows, nrows, cstart, J));
2644     }
2645   }
2646 
2647   /* Set matrix entries for vertices */
2648   /*---------------------------------*/
2649   for (v = vStart; v < vEnd; v++) {
2650     /* Get row indices */
2651     PetscCall(DMNetworkGetGlobalVecOffset(dm, v, ALL_COMPONENTS, &rstart));
2652     PetscCall(PetscSectionGetDof(network->DofSection, v, &nrows));
2653     if (!nrows) continue;
2654 
2655     PetscCall(DMNetworkIsGhostVertex(dm, v, &ghost));
2656     if (ghost) {
2657       PetscCall(PetscMalloc1(nrows, &rows_v));
2658     } else {
2659       rows_v = rows;
2660     }
2661     for (j = 0; j < nrows; j++) rows_v[j] = j + rstart;
2662 
2663     /* Get supporting edges and connected vertices */
2664     PetscCall(DMNetworkGetSupportingEdges(dm, v, &nedges, &edges));
2665 
2666     for (e = 0; e < nedges; e++) {
2667       /* Supporting edges */
2668       PetscCall(DMNetworkGetGlobalVecOffset(dm, edges[e], ALL_COMPONENTS, &cstart));
2669       PetscCall(PetscSectionGetDof(network->DofSection, edges[e], &ncols));
2670 
2671       if (network->Jv) {
2672         Juser = network->Jv[vptr[v - vStart] + 2 * e + 1]; /* Jacobian(v,e) */
2673       } else Juser = NULL;
2674       PetscCall(MatSetblock_private(Juser, nrows, rows_v, ncols, cstart, J));
2675 
2676       /* Connected vertices */
2677       PetscCall(DMNetworkGetConnectedVertices(dm, edges[e], &cone));
2678       vc = (v == cone[0]) ? cone[1] : cone[0];
2679 
2680       PetscCall(DMNetworkGetGlobalVecOffset(dm, vc, ALL_COMPONENTS, &cstart));
2681       PetscCall(PetscSectionGetDof(network->DofSection, vc, &ncols));
2682 
2683       if (network->Jv) {
2684         Juser = network->Jv[vptr[v - vStart] + 2 * e + 2]; /* Jacobian(v,vc) */
2685       } else Juser = NULL;
2686       PetscCall(MatSetblock_private(Juser, nrows, rows_v, ncols, cstart, J));
2687     }
2688 
2689     /* Set matrix entries for vertex self */
2690     if (!ghost) {
2691       PetscCall(DMNetworkGetGlobalVecOffset(dm, v, ALL_COMPONENTS, &cstart));
2692       if (network->Jv) {
2693         Juser = network->Jv[vptr[v - vStart]]; /* Jacobian(v,v) */
2694       } else Juser = NULL;
2695       PetscCall(MatSetblock_private(Juser, nrows, rows_v, nrows, cstart, J));
2696     }
2697     if (ghost) PetscCall(PetscFree(rows_v));
2698   }
2699   PetscCall(PetscFree(rows));
2700 
2701   PetscCall(MatAssemblyBegin(*J, MAT_FINAL_ASSEMBLY));
2702   PetscCall(MatAssemblyEnd(*J, MAT_FINAL_ASSEMBLY));
2703 
2704   PetscCall(MatSetDM(*J, dm));
2705   PetscFunctionReturn(0);
2706 }
2707 
2708 static PetscErrorCode DMNetworkDestroyComponentData(DM dm)
2709 {
2710   DM_Network *network = (DM_Network *)dm->data;
2711   PetscInt    j, np;
2712 
2713   PetscFunctionBegin;
2714   if (network->header) {
2715     np = network->cloneshared->pEnd - network->cloneshared->pStart;
2716     for (j = 0; j < np; j++) {
2717       PetscCall(PetscFree5(network->header[j].size, network->header[j].key, network->header[j].offset, network->header[j].nvar, network->header[j].offsetvarrel));
2718       PetscCall(PetscFree(network->cvalue[j].data));
2719     }
2720     PetscCall(PetscFree2(network->header, network->cvalue));
2721   }
2722   PetscFunctionReturn(0);
2723 }
2724 
2725 PetscErrorCode DMDestroy_Network(DM dm)
2726 {
2727   DM_Network *network = (DM_Network *)dm->data;
2728   PetscInt    j;
2729 
2730   PetscFunctionBegin;
2731   /*
2732     Developers Note: Due to the mixed topological definition of DMNetwork and data defined ON the
2733     network like DofSection, DataSection, *componentdataarray, and friends, when cloning, we share
2734     only the true topological data, and make our own data ON the network. Thus refct only refers
2735     to the number of references to topological data, and data ON the network is always destroyed.
2736     It is understood this is atypical for a DM, but is very intentional.
2737   */
2738 
2739   /* Always destroy data ON the network */
2740   PetscCall(PetscFree(network->Je));
2741   if (network->Jv) {
2742     PetscCall(PetscFree(network->Jvptr));
2743     PetscCall(PetscFree(network->Jv));
2744   }
2745   PetscCall(PetscSectionDestroy(&network->DataSection));
2746   PetscCall(PetscSectionDestroy(&network->DofSection));
2747   PetscCall(PetscFree(network->component));
2748   PetscCall(PetscFree(network->componentdataarray));
2749   PetscCall(DMNetworkDestroyComponentData(dm));
2750 
2751   PetscCall(DMDestroy(&network->plex)); /* this is cloned in DMClone_Network, so safe to destroy */
2752 
2753   /*
2754   Developers Note: The DMNetworkVertexInfo and DMNetworkEdgeInfo data structures are completely
2755   destroyed as they are again a mix of topological data:
2756     ISLocalToGlobalMapping            mapping;
2757     PetscSF                           sf;
2758   and data ON the network
2759     PetscSection                      DofSection;
2760     PetscSection                      GlobalDofSection;
2761   And the only way to access them currently is through DMNetworkAssembleGraphStructures which assembles
2762   everything. So we must destroy everything and require DMNetworkAssembleGraphStructures is called again
2763   for any clone.
2764   */
2765   PetscCall(ISLocalToGlobalMappingDestroy(&network->vertex.mapping));
2766   PetscCall(PetscSectionDestroy(&network->vertex.DofSection));
2767   PetscCall(PetscSectionDestroy(&network->vertex.GlobalDofSection));
2768   PetscCall(PetscSFDestroy(&network->vertex.sf));
2769   /* edge */
2770   PetscCall(ISLocalToGlobalMappingDestroy(&network->edge.mapping));
2771   PetscCall(PetscSectionDestroy(&network->edge.DofSection));
2772   PetscCall(PetscSectionDestroy(&network->edge.GlobalDofSection));
2773   PetscCall(PetscSFDestroy(&network->edge.sf));
2774   /* Destroy the potentially cloneshared data */
2775   if (--network->cloneshared->refct <= 0) {
2776     /* Developer Note: I'm not sure if vltog can be reused or not, as I'm not sure what it's purpose is. I
2777      naively think it can be reused. */
2778     PetscCall(PetscFree(network->cloneshared->vltog));
2779     for (j = 0; j < network->cloneshared->Nsvtx; j++) PetscCall(PetscFree(network->cloneshared->svtx[j].sv));
2780     PetscCall(PetscFree(network->cloneshared->svtx));
2781     PetscCall(PetscFree2(network->cloneshared->subnetedge, network->cloneshared->subnetvtx));
2782     PetscCall(PetscTableDestroy(&network->cloneshared->svtable));
2783     PetscCall(PetscFree(network->cloneshared->subnet));
2784     PetscCall(PetscFree(network->cloneshared));
2785   }
2786   PetscCall(PetscFree(network)); /* Always freed as this structure is copied in a clone, not cloneshared */
2787   PetscFunctionReturn(0);
2788 }
2789 
2790 PetscErrorCode DMView_Network(DM dm, PetscViewer viewer)
2791 {
2792   PetscBool   iascii;
2793   PetscMPIInt rank;
2794 
2795   PetscFunctionBegin;
2796   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2797   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2798   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
2799   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
2800   if (iascii) {
2801     const PetscInt *cone, *vtx, *edges;
2802     PetscInt        vfrom, vto, i, j, nv, ne, nsv, p, nsubnet;
2803     DM_Network     *network = (DM_Network *)dm->data;
2804 
2805     nsubnet = network->cloneshared->Nsubnet; /* num of subnetworks */
2806     if (rank == 0) {
2807       PetscCall(PetscPrintf(PETSC_COMM_SELF, "  NSubnets: %" PetscInt_FMT "; NEdges: %" PetscInt_FMT "; NVertices: %" PetscInt_FMT "; NSharedVertices: %" PetscInt_FMT ".\n", nsubnet, network->cloneshared->NEdges, network->cloneshared->NVertices,
2808                             network->cloneshared->Nsvtx));
2809     }
2810 
2811     PetscCall(DMNetworkGetSharedVertices(dm, &nsv, NULL));
2812     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
2813     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  [%d] nEdges: %" PetscInt_FMT "; nVertices: %" PetscInt_FMT "; nSharedVertices: %" PetscInt_FMT "\n", rank, network->cloneshared->nEdges, network->cloneshared->nVertices, nsv));
2814 
2815     for (i = 0; i < nsubnet; i++) {
2816       PetscCall(DMNetworkGetSubnetwork(dm, i, &nv, &ne, &vtx, &edges));
2817       if (ne) {
2818         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "     Subnet %" PetscInt_FMT ": nEdges %" PetscInt_FMT ", nVertices(include shared vertices) %" PetscInt_FMT "\n", i, ne, nv));
2819         for (j = 0; j < ne; j++) {
2820           p = edges[j];
2821           PetscCall(DMNetworkGetConnectedVertices(dm, p, &cone));
2822           PetscCall(DMNetworkGetGlobalVertexIndex(dm, cone[0], &vfrom));
2823           PetscCall(DMNetworkGetGlobalVertexIndex(dm, cone[1], &vto));
2824           PetscCall(DMNetworkGetGlobalEdgeIndex(dm, edges[j], &p));
2825           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "       edge %" PetscInt_FMT ": %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", p, vfrom, vto));
2826         }
2827       }
2828     }
2829 
2830     /* Shared vertices */
2831     PetscCall(DMNetworkGetSharedVertices(dm, NULL, &vtx));
2832     if (nsv) {
2833       PetscInt        gidx;
2834       PetscBool       ghost;
2835       const PetscInt *sv = NULL;
2836 
2837       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "     SharedVertices:\n"));
2838       for (i = 0; i < nsv; i++) {
2839         PetscCall(DMNetworkIsGhostVertex(dm, vtx[i], &ghost));
2840         if (ghost) continue;
2841 
2842         PetscCall(DMNetworkSharedVertexGetInfo(dm, vtx[i], &gidx, &nv, &sv));
2843         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "       svtx %" PetscInt_FMT ": global index %" PetscInt_FMT ", subnet[%" PetscInt_FMT "].%" PetscInt_FMT " ---->\n", i, gidx, sv[0], sv[1]));
2844         for (j = 1; j < nv; j++) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "                                           ----> subnet[%" PetscInt_FMT "].%" PetscInt_FMT "\n", sv[2 * j], sv[2 * j + 1]));
2845       }
2846     }
2847     PetscCall(PetscViewerFlush(viewer));
2848     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
2849   } else PetscCheck(iascii, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMNetwork writing", ((PetscObject)viewer)->type_name);
2850   PetscFunctionReturn(0);
2851 }
2852 
2853 PetscErrorCode DMGlobalToLocalBegin_Network(DM dm, Vec g, InsertMode mode, Vec l)
2854 {
2855   DM_Network *network = (DM_Network *)dm->data;
2856 
2857   PetscFunctionBegin;
2858   PetscCall(DMGlobalToLocalBegin(network->plex, g, mode, l));
2859   PetscFunctionReturn(0);
2860 }
2861 
2862 PetscErrorCode DMGlobalToLocalEnd_Network(DM dm, Vec g, InsertMode mode, Vec l)
2863 {
2864   DM_Network *network = (DM_Network *)dm->data;
2865 
2866   PetscFunctionBegin;
2867   PetscCall(DMGlobalToLocalEnd(network->plex, g, mode, l));
2868   PetscFunctionReturn(0);
2869 }
2870 
2871 PetscErrorCode DMLocalToGlobalBegin_Network(DM dm, Vec l, InsertMode mode, Vec g)
2872 {
2873   DM_Network *network = (DM_Network *)dm->data;
2874 
2875   PetscFunctionBegin;
2876   PetscCall(DMLocalToGlobalBegin(network->plex, l, mode, g));
2877   PetscFunctionReturn(0);
2878 }
2879 
2880 PetscErrorCode DMLocalToGlobalEnd_Network(DM dm, Vec l, InsertMode mode, Vec g)
2881 {
2882   DM_Network *network = (DM_Network *)dm->data;
2883 
2884   PetscFunctionBegin;
2885   PetscCall(DMLocalToGlobalEnd(network->plex, l, mode, g));
2886   PetscFunctionReturn(0);
2887 }
2888 
2889 /*@
2890   DMNetworkGetVertexLocalToGlobalOrdering - Get vertex global index
2891 
2892   Not collective
2893 
2894   Input Parameters:
2895 + dm - the dm object
2896 - vloc - local vertex ordering, start from 0
2897 
2898   Output Parameters:
2899 .  vg  - global vertex ordering, start from 0
2900 
2901   Level: advanced
2902 
2903 .seealso: `DMNetworkSetVertexLocalToGlobalOrdering()`
2904 @*/
2905 PetscErrorCode DMNetworkGetVertexLocalToGlobalOrdering(DM dm, PetscInt vloc, PetscInt *vg)
2906 {
2907   DM_Network *network = (DM_Network *)dm->data;
2908   PetscInt   *vltog   = network->cloneshared->vltog;
2909 
2910   PetscFunctionBegin;
2911   PetscCheck(vltog, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Must call DMNetworkSetVertexLocalToGlobalOrdering() first");
2912   *vg = vltog[vloc];
2913   PetscFunctionReturn(0);
2914 }
2915 
2916 /*@
2917   DMNetworkSetVertexLocalToGlobalOrdering - Create and setup vertex local to global map
2918 
2919   Collective
2920 
2921   Input Parameters:
2922 . dm - the dm object
2923 
2924   Level: advanced
2925 
2926 .seealso: `DMNetworkGetGlobalVertexIndex()`
2927 @*/
2928 PetscErrorCode DMNetworkSetVertexLocalToGlobalOrdering(DM dm)
2929 {
2930   DM_Network        *network = (DM_Network *)dm->data;
2931   MPI_Comm           comm;
2932   PetscMPIInt        rank, size, *displs = NULL, *recvcounts = NULL, remoterank;
2933   PetscBool          ghost;
2934   PetscInt          *vltog, nroots, nleaves, i, *vrange, k, N, lidx;
2935   const PetscSFNode *iremote;
2936   PetscSF            vsf;
2937   Vec                Vleaves, Vleaves_seq;
2938   VecScatter         ctx;
2939   PetscScalar       *varr, val;
2940   const PetscScalar *varr_read;
2941 
2942   PetscFunctionBegin;
2943   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
2944   PetscCallMPI(MPI_Comm_size(comm, &size));
2945   PetscCallMPI(MPI_Comm_rank(comm, &rank));
2946 
2947   if (size == 1) {
2948     nroots = network->cloneshared->vEnd - network->cloneshared->vStart;
2949     PetscCall(PetscMalloc1(nroots, &vltog));
2950     for (i = 0; i < nroots; i++) vltog[i] = i;
2951     network->cloneshared->vltog = vltog;
2952     PetscFunctionReturn(0);
2953   }
2954 
2955   PetscCheck(network->cloneshared->distributecalled, comm, PETSC_ERR_ARG_WRONGSTATE, "Must call DMNetworkDistribute() first");
2956   if (network->cloneshared->vltog) PetscCall(PetscFree(network->cloneshared->vltog));
2957 
2958   PetscCall(DMNetworkSetSubMap_private(network->cloneshared->vStart, network->cloneshared->vEnd, &network->vertex.mapping));
2959   PetscCall(PetscSFGetSubSF(network->plex->sf, network->vertex.mapping, &network->vertex.sf));
2960   vsf = network->vertex.sf;
2961 
2962   PetscCall(PetscMalloc3(size + 1, &vrange, size, &displs, size, &recvcounts));
2963   PetscCall(PetscSFGetGraph(vsf, &nroots, &nleaves, NULL, &iremote));
2964 
2965   for (i = 0; i < size; i++) {
2966     displs[i]     = i;
2967     recvcounts[i] = 1;
2968   }
2969 
2970   i         = nroots - nleaves; /* local number of vertices, excluding ghosts */
2971   vrange[0] = 0;
2972   PetscCallMPI(MPI_Allgatherv(&i, 1, MPIU_INT, vrange + 1, recvcounts, displs, MPIU_INT, comm));
2973   for (i = 2; i < size + 1; i++) vrange[i] += vrange[i - 1];
2974 
2975   PetscCall(PetscMalloc1(nroots, &vltog));
2976   network->cloneshared->vltog = vltog;
2977 
2978   /* Set vltog for non-ghost vertices */
2979   k = 0;
2980   for (i = 0; i < nroots; i++) {
2981     PetscCall(DMNetworkIsGhostVertex(dm, i + network->cloneshared->vStart, &ghost));
2982     if (ghost) continue;
2983     vltog[i] = vrange[rank] + k++;
2984   }
2985   PetscCall(PetscFree3(vrange, displs, recvcounts));
2986 
2987   /* Set vltog for ghost vertices */
2988   /* (a) create parallel Vleaves and sequential Vleaves_seq to convert local iremote[*].index to global index */
2989   PetscCall(VecCreate(comm, &Vleaves));
2990   PetscCall(VecSetSizes(Vleaves, 2 * nleaves, PETSC_DETERMINE));
2991   PetscCall(VecSetFromOptions(Vleaves));
2992   PetscCall(VecGetArray(Vleaves, &varr));
2993   for (i = 0; i < nleaves; i++) {
2994     varr[2 * i]     = (PetscScalar)(iremote[i].rank);  /* rank of remote process */
2995     varr[2 * i + 1] = (PetscScalar)(iremote[i].index); /* local index in remote process */
2996   }
2997   PetscCall(VecRestoreArray(Vleaves, &varr));
2998 
2999   /* (b) scatter local info to remote processes via VecScatter() */
3000   PetscCall(VecScatterCreateToAll(Vleaves, &ctx, &Vleaves_seq));
3001   PetscCall(VecScatterBegin(ctx, Vleaves, Vleaves_seq, INSERT_VALUES, SCATTER_FORWARD));
3002   PetscCall(VecScatterEnd(ctx, Vleaves, Vleaves_seq, INSERT_VALUES, SCATTER_FORWARD));
3003 
3004   /* (c) convert local indices to global indices in parallel vector Vleaves */
3005   PetscCall(VecGetSize(Vleaves_seq, &N));
3006   PetscCall(VecGetArrayRead(Vleaves_seq, &varr_read));
3007   for (i = 0; i < N; i += 2) {
3008     remoterank = (PetscMPIInt)PetscRealPart(varr_read[i]);
3009     if (remoterank == rank) {
3010       k    = i + 1; /* row number */
3011       lidx = (PetscInt)PetscRealPart(varr_read[i + 1]);
3012       val  = (PetscScalar)vltog[lidx]; /* global index for non-ghost vertex computed above */
3013       PetscCall(VecSetValues(Vleaves, 1, &k, &val, INSERT_VALUES));
3014     }
3015   }
3016   PetscCall(VecRestoreArrayRead(Vleaves_seq, &varr_read));
3017   PetscCall(VecAssemblyBegin(Vleaves));
3018   PetscCall(VecAssemblyEnd(Vleaves));
3019 
3020   /* (d) Set vltog for ghost vertices by copying local values of Vleaves */
3021   PetscCall(VecGetArrayRead(Vleaves, &varr_read));
3022   k = 0;
3023   for (i = 0; i < nroots; i++) {
3024     PetscCall(DMNetworkIsGhostVertex(dm, i + network->cloneshared->vStart, &ghost));
3025     if (!ghost) continue;
3026     vltog[i] = (PetscInt)PetscRealPart(varr_read[2 * k + 1]);
3027     k++;
3028   }
3029   PetscCall(VecRestoreArrayRead(Vleaves, &varr_read));
3030 
3031   PetscCall(VecDestroy(&Vleaves));
3032   PetscCall(VecDestroy(&Vleaves_seq));
3033   PetscCall(VecScatterDestroy(&ctx));
3034   PetscFunctionReturn(0);
3035 }
3036 
3037 static inline PetscErrorCode DMISAddSize_private(DM_Network *network, PetscInt p, PetscInt numkeys, PetscInt keys[], PetscInt blocksize[], PetscInt nselectedvar[], PetscInt *nidx)
3038 {
3039   PetscInt                 i, j, ncomps, nvar, key, offset = 0;
3040   DMNetworkComponentHeader header;
3041 
3042   PetscFunctionBegin;
3043   PetscCall(PetscSectionGetOffset(network->DataSection, p, &offset));
3044   ncomps = ((DMNetworkComponentHeader)(network->componentdataarray + offset))->ndata;
3045   header = (DMNetworkComponentHeader)(network->componentdataarray + offset);
3046 
3047   for (i = 0; i < ncomps; i++) {
3048     key  = header->key[i];
3049     nvar = header->nvar[i];
3050     for (j = 0; j < numkeys; j++) {
3051       if (key == keys[j]) {
3052         if (!blocksize || blocksize[j] == -1) {
3053           *nidx += nvar;
3054         } else {
3055           *nidx += nselectedvar[j] * nvar / blocksize[j];
3056         }
3057       }
3058     }
3059   }
3060   PetscFunctionReturn(0);
3061 }
3062 
3063 static inline PetscErrorCode DMISComputeIdx_private(DM dm, PetscInt p, PetscInt numkeys, PetscInt keys[], PetscInt blocksize[], PetscInt nselectedvar[], PetscInt *selectedvar[], PetscInt *ii, PetscInt *idx)
3064 {
3065   PetscInt                 i, j, ncomps, nvar, key, offsetg, k, k1, offset = 0;
3066   DM_Network              *network = (DM_Network *)dm->data;
3067   DMNetworkComponentHeader header;
3068 
3069   PetscFunctionBegin;
3070   PetscCall(PetscSectionGetOffset(network->DataSection, p, &offset));
3071   ncomps = ((DMNetworkComponentHeader)(network->componentdataarray + offset))->ndata;
3072   header = (DMNetworkComponentHeader)(network->componentdataarray + offset);
3073 
3074   for (i = 0; i < ncomps; i++) {
3075     key  = header->key[i];
3076     nvar = header->nvar[i];
3077     for (j = 0; j < numkeys; j++) {
3078       if (key != keys[j]) continue;
3079 
3080       PetscCall(DMNetworkGetGlobalVecOffset(dm, p, i, &offsetg));
3081       if (!blocksize || blocksize[j] == -1) {
3082         for (k = 0; k < nvar; k++) idx[(*ii)++] = offsetg + k;
3083       } else {
3084         for (k = 0; k < nvar; k += blocksize[j]) {
3085           for (k1 = 0; k1 < nselectedvar[j]; k1++) idx[(*ii)++] = offsetg + k + selectedvar[j][k1];
3086         }
3087       }
3088     }
3089   }
3090   PetscFunctionReturn(0);
3091 }
3092 
3093 /*@
3094   DMNetworkCreateIS - Create an index set object from the global vector of the network
3095 
3096   Collective
3097 
3098   Input Parameters:
3099 + dm - DMNetwork object
3100 . numkeys - number of keys
3101 . keys - array of keys that define the components of the variables you wish to extract
3102 . blocksize - block size of the variables associated to the component
3103 . nselectedvar - number of variables in each block to select
3104 - selectedvar - the offset into the block of each variable in each block to select
3105 
3106   Output Parameters:
3107 . is - the index set
3108 
3109   Level: Advanced
3110 
3111   Notes:
3112     Use blocksize[i] of -1 to indicate select all the variables of the i-th component, for which nselectvar[i] and selectedvar[i] are ignored. Use NULL, NULL, NULL to indicate for all selected components one wishes to obtain all the values of that component. For example, DMNetworkCreateIS(dm,1,&key,NULL,NULL,NULL,&is) will return an is that extracts all the variables for the 0-th component.
3113 
3114 .seealso: `DMNetworkCreate()`, `ISCreateGeneral()`, `DMNetworkCreateLocalIS()`
3115 @*/
3116 PetscErrorCode DMNetworkCreateIS(DM dm, PetscInt numkeys, PetscInt keys[], PetscInt blocksize[], PetscInt nselectedvar[], PetscInt *selectedvar[], IS *is)
3117 {
3118   MPI_Comm    comm;
3119   DM_Network *network = (DM_Network *)dm->data;
3120   PetscInt    i, p, estart, eend, vstart, vend, nidx, *idx;
3121   PetscBool   ghost;
3122 
3123   PetscFunctionBegin;
3124   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
3125 
3126   /* Check input parameters */
3127   for (i = 0; i < numkeys; i++) {
3128     if (!blocksize || blocksize[i] == -1) continue;
3129     PetscCheck(nselectedvar[i] <= blocksize[i], PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "number of selectedvariables %" PetscInt_FMT " cannot be larger than blocksize %" PetscInt_FMT, nselectedvar[i], blocksize[i]);
3130   }
3131 
3132   PetscCall(DMNetworkGetEdgeRange(dm, &estart, &eend));
3133   PetscCall(DMNetworkGetVertexRange(dm, &vstart, &vend));
3134 
3135   /* Get local number of idx */
3136   nidx = 0;
3137   for (p = estart; p < eend; p++) PetscCall(DMISAddSize_private(network, p, numkeys, keys, blocksize, nselectedvar, &nidx));
3138   for (p = vstart; p < vend; p++) {
3139     PetscCall(DMNetworkIsGhostVertex(dm, p, &ghost));
3140     if (ghost) continue;
3141     PetscCall(DMISAddSize_private(network, p, numkeys, keys, blocksize, nselectedvar, &nidx));
3142   }
3143 
3144   /* Compute idx */
3145   PetscCall(PetscMalloc1(nidx, &idx));
3146   i = 0;
3147   for (p = estart; p < eend; p++) PetscCall(DMISComputeIdx_private(dm, p, numkeys, keys, blocksize, nselectedvar, selectedvar, &i, idx));
3148   for (p = vstart; p < vend; p++) {
3149     PetscCall(DMNetworkIsGhostVertex(dm, p, &ghost));
3150     if (ghost) continue;
3151     PetscCall(DMISComputeIdx_private(dm, p, numkeys, keys, blocksize, nselectedvar, selectedvar, &i, idx));
3152   }
3153 
3154   /* Create is */
3155   PetscCall(ISCreateGeneral(comm, nidx, idx, PETSC_COPY_VALUES, is));
3156   PetscCall(PetscFree(idx));
3157   PetscFunctionReturn(0);
3158 }
3159 
3160 static inline PetscErrorCode DMISComputeLocalIdx_private(DM dm, PetscInt p, PetscInt numkeys, PetscInt keys[], PetscInt blocksize[], PetscInt nselectedvar[], PetscInt *selectedvar[], PetscInt *ii, PetscInt *idx)
3161 {
3162   PetscInt                 i, j, ncomps, nvar, key, offsetl, k, k1, offset = 0;
3163   DM_Network              *network = (DM_Network *)dm->data;
3164   DMNetworkComponentHeader header;
3165 
3166   PetscFunctionBegin;
3167   PetscCall(PetscSectionGetOffset(network->DataSection, p, &offset));
3168   ncomps = ((DMNetworkComponentHeader)(network->componentdataarray + offset))->ndata;
3169   header = (DMNetworkComponentHeader)(network->componentdataarray + offset);
3170 
3171   for (i = 0; i < ncomps; i++) {
3172     key  = header->key[i];
3173     nvar = header->nvar[i];
3174     for (j = 0; j < numkeys; j++) {
3175       if (key != keys[j]) continue;
3176 
3177       PetscCall(DMNetworkGetLocalVecOffset(dm, p, i, &offsetl));
3178       if (!blocksize || blocksize[j] == -1) {
3179         for (k = 0; k < nvar; k++) idx[(*ii)++] = offsetl + k;
3180       } else {
3181         for (k = 0; k < nvar; k += blocksize[j]) {
3182           for (k1 = 0; k1 < nselectedvar[j]; k1++) idx[(*ii)++] = offsetl + k + selectedvar[j][k1];
3183         }
3184       }
3185     }
3186   }
3187   PetscFunctionReturn(0);
3188 }
3189 
3190 /*@
3191   DMNetworkCreateLocalIS - Create an index set object from the local vector of the network
3192 
3193   Not collective
3194 
3195   Input Parameters:
3196 + dm - DMNetwork object
3197 . numkeys - number of keys
3198 . keys - array of keys that define the components of the variables you wish to extract
3199 . blocksize - block size of the variables associated to the component
3200 . nselectedvar - number of variables in each block to select
3201 - selectedvar - the offset into the block of each variable in each block to select
3202 
3203   Output Parameters:
3204 . is - the index set
3205 
3206   Level: Advanced
3207 
3208   Notes:
3209     Use blocksize[i] of -1 to indicate select all the variables of the i-th component, for which nselectvar[i] and selectedvar[i] are ignored. Use NULL, NULL, NULL to indicate for all selected components one wishes to obtain all the values of that component. For example, DMNetworkCreateLocalIS(dm,1,&key,NULL,NULL,NULL,&is) will return an is that extracts all the variables for the 0-th component.
3210 
3211 .seealso: `DMNetworkCreate()`, `DMNetworkCreateIS`, `ISCreateGeneral()`
3212 @*/
3213 PetscErrorCode DMNetworkCreateLocalIS(DM dm, PetscInt numkeys, PetscInt keys[], PetscInt blocksize[], PetscInt nselectedvar[], PetscInt *selectedvar[], IS *is)
3214 {
3215   DM_Network *network = (DM_Network *)dm->data;
3216   PetscInt    i, p, pstart, pend, nidx, *idx;
3217 
3218   PetscFunctionBegin;
3219   /* Check input parameters */
3220   for (i = 0; i < numkeys; i++) {
3221     if (!blocksize || blocksize[i] == -1) continue;
3222     PetscCheck(nselectedvar[i] <= blocksize[i], PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "number of selectedvariables %" PetscInt_FMT " cannot be larger than blocksize %" PetscInt_FMT, nselectedvar[i], blocksize[i]);
3223   }
3224 
3225   pstart = network->cloneshared->pStart;
3226   pend   = network->cloneshared->pEnd;
3227 
3228   /* Get local number of idx */
3229   nidx = 0;
3230   for (p = pstart; p < pend; p++) PetscCall(DMISAddSize_private(network, p, numkeys, keys, blocksize, nselectedvar, &nidx));
3231 
3232   /* Compute local idx */
3233   PetscCall(PetscMalloc1(nidx, &idx));
3234   i = 0;
3235   for (p = pstart; p < pend; p++) PetscCall(DMISComputeLocalIdx_private(dm, p, numkeys, keys, blocksize, nselectedvar, selectedvar, &i, idx));
3236 
3237   /* Create is */
3238   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, nidx, idx, PETSC_COPY_VALUES, is));
3239   PetscCall(PetscFree(idx));
3240   PetscFunctionReturn(0);
3241 }
3242 /*@
3243   DMNetworkFinalizeComponents - Sets up internal data structures for the sections and components. It is called after registering new components and adding all components
3244   to the cloned network. After calling this subroutine, no new components can be added to the network.
3245 
3246   Collective
3247 
3248   Input Parameters:
3249 . dm - the dmnetwork object
3250 
3251   Level: beginner
3252 
3253 .seealso: `DMNetworkAddComponent()`, `DMNetworkRegisterComponent()`, `DMSetUp()`
3254 @*/
3255 PetscErrorCode DMNetworkFinalizeComponents(DM dm)
3256 {
3257   DM_Network *network = (DM_Network *)dm->data;
3258 
3259   PetscFunctionBegin;
3260   if (network->componentsetup) PetscFunctionReturn(0);
3261   PetscCall(DMNetworkComponentSetUp(dm));
3262   PetscCall(DMNetworkVariablesSetUp(dm));
3263   PetscCall(DMSetLocalSection(network->plex, network->DofSection));
3264   PetscCall(DMGetGlobalSection(network->plex, &network->GlobalDofSection));
3265   network->componentsetup = PETSC_TRUE;
3266   PetscFunctionReturn(0);
3267 }
3268