xref: /petsc/src/dm/impls/network/network.c (revision da81f9329be15cc55f054c8a00978087195c9247)
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 determined 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   DMNetworkGetVertexRange - Get the bounds [start, end) for the local vertices
960 
961   Not Collective
962 
963   Input Parameter:
964 . dm - the DMNetwork object
965 
966   Output Parameters:
967 + vStart - the first vertex point
968 - vEnd - one beyond the last vertex point
969 
970   Level: beginner
971 
972 .seealso: `DMNetworkGetEdgeRange()`
973 @*/
974 PetscErrorCode DMNetworkGetVertexRange(DM dm, PetscInt *vStart, PetscInt *vEnd)
975 {
976   DM_Network *network = (DM_Network *)dm->data;
977 
978   PetscFunctionBegin;
979   if (vStart) *vStart = network->cloneshared->vStart;
980   if (vEnd) *vEnd = network->cloneshared->vEnd;
981   PetscFunctionReturn(0);
982 }
983 
984 /*@
985   DMNetworkGetEdgeRange - Get the bounds [start, end) for the local edges
986 
987   Not Collective
988 
989   Input Parameter:
990 . dm - the DMNetwork object
991 
992   Output Parameters:
993 + eStart - The first edge point
994 - eEnd - One beyond the last edge point
995 
996   Level: beginner
997 
998 .seealso: `DMNetworkGetVertexRange()`
999 @*/
1000 PetscErrorCode DMNetworkGetEdgeRange(DM dm, PetscInt *eStart, PetscInt *eEnd)
1001 {
1002   DM_Network *network = (DM_Network *)dm->data;
1003 
1004   PetscFunctionBegin;
1005   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1006   if (eStart) *eStart = network->cloneshared->eStart;
1007   if (eEnd) *eEnd = network->cloneshared->eEnd;
1008   PetscFunctionReturn(0);
1009 }
1010 
1011 PetscErrorCode DMNetworkGetIndex(DM dm, PetscInt p, PetscInt *index)
1012 {
1013   DM_Network *network = (DM_Network *)dm->data;
1014 
1015   PetscFunctionBegin;
1016   if (network->header) {
1017     *index = network->header[p].index;
1018   } else {
1019     PetscInt                 offsetp;
1020     DMNetworkComponentHeader header;
1021 
1022     PetscCall(PetscSectionGetOffset(network->DataSection, p, &offsetp));
1023     header = (DMNetworkComponentHeader)(network->componentdataarray + offsetp);
1024     *index = header->index;
1025   }
1026   PetscFunctionReturn(0);
1027 }
1028 
1029 PetscErrorCode DMNetworkGetSubnetID(DM dm, PetscInt p, PetscInt *subnetid)
1030 {
1031   DM_Network *network = (DM_Network *)dm->data;
1032 
1033   PetscFunctionBegin;
1034   if (network->header) {
1035     *subnetid = network->header[p].subnetid;
1036   } else {
1037     PetscInt                 offsetp;
1038     DMNetworkComponentHeader header;
1039 
1040     PetscCall(PetscSectionGetOffset(network->DataSection, p, &offsetp));
1041     header    = (DMNetworkComponentHeader)(network->componentdataarray + offsetp);
1042     *subnetid = header->subnetid;
1043   }
1044   PetscFunctionReturn(0);
1045 }
1046 
1047 /*@
1048   DMNetworkGetGlobalEdgeIndex - Get the global numbering for the edge on the network
1049 
1050   Not Collective
1051 
1052   Input Parameters:
1053 + dm - DMNetwork object
1054 - p - edge point
1055 
1056   Output Parameters:
1057 . index - the global numbering for the edge
1058 
1059   Level: intermediate
1060 
1061 .seealso: `DMNetworkGetGlobalVertexIndex()`
1062 @*/
1063 PetscErrorCode DMNetworkGetGlobalEdgeIndex(DM dm, PetscInt p, PetscInt *index)
1064 {
1065   PetscFunctionBegin;
1066   PetscCall(DMNetworkGetIndex(dm, p, index));
1067   PetscFunctionReturn(0);
1068 }
1069 
1070 /*@
1071   DMNetworkGetGlobalVertexIndex - Get the global numbering for the vertex on the network
1072 
1073   Not Collective
1074 
1075   Input Parameters:
1076 + dm - DMNetwork object
1077 - p  - vertex point
1078 
1079   Output Parameters:
1080 . index - the global numbering for the vertex
1081 
1082   Level: intermediate
1083 
1084 .seealso: `DMNetworkGetGlobalEdgeIndex()`, `DMNetworkGetLocalVertexIndex()`
1085 @*/
1086 PetscErrorCode DMNetworkGetGlobalVertexIndex(DM dm, PetscInt p, PetscInt *index)
1087 {
1088   PetscFunctionBegin;
1089   PetscCall(DMNetworkGetIndex(dm, p, index));
1090   PetscFunctionReturn(0);
1091 }
1092 
1093 /*@
1094   DMNetworkGetNumComponents - Get the number of components at a vertex/edge
1095 
1096   Not Collective
1097 
1098   Input Parameters:
1099 + dm - the DMNetwork object
1100 - p - vertex/edge point
1101 
1102   Output Parameters:
1103 . numcomponents - Number of components at the vertex/edge
1104 
1105   Level: beginner
1106 
1107 .seealso: `DMNetworkRegisterComponent()`, `DMNetworkAddComponent()`
1108 @*/
1109 PetscErrorCode DMNetworkGetNumComponents(DM dm, PetscInt p, PetscInt *numcomponents)
1110 {
1111   PetscInt    offset;
1112   DM_Network *network = (DM_Network *)dm->data;
1113 
1114   PetscFunctionBegin;
1115   PetscCall(PetscSectionGetOffset(network->DataSection, p, &offset));
1116   *numcomponents = ((DMNetworkComponentHeader)(network->componentdataarray + offset))->ndata;
1117   PetscFunctionReturn(0);
1118 }
1119 
1120 /*@
1121   DMNetworkGetLocalVecOffset - Get the offset for accessing the variables associated with a component at the given vertex/edge from the local vector
1122 
1123   Not Collective
1124 
1125   Input Parameters:
1126 + dm - the DMNetwork object
1127 . p - the edge or vertex point
1128 - compnum - component number; use ALL_COMPONENTS if no specific component is requested
1129 
1130   Output Parameters:
1131 . offset - the local offset
1132 
1133   Notes:
1134     These offsets can be passed to MatSetValuesLocal() for matrices obtained with DMCreateMatrix().
1135 
1136     For vectors obtained with DMCreateLocalVector() the offsets can be used with VecSetValues().
1137 
1138     For vectors obtained with DMCreateLocalVector() and the array obtained with VecGetArray(vec,&array) you can access or set
1139     the vector values with array[offset].
1140 
1141     For vectors obtained with DMCreateGlobalVector() the offsets can be used with VecSetValuesLocal().
1142 
1143   Level: intermediate
1144 
1145 .seealso: `DMGetLocalVector()`, `DMNetworkGetComponent()`, `DMNetworkGetGlobalVecOffset()`, `DMCreateGlobalVector()`, `VecGetArray()`, `VecSetValuesLocal()`, `MatSetValuesLocal()`
1146 @*/
1147 PetscErrorCode DMNetworkGetLocalVecOffset(DM dm, PetscInt p, PetscInt compnum, PetscInt *offset)
1148 {
1149   DM_Network              *network = (DM_Network *)dm->data;
1150   PetscInt                 offsetp, offsetd;
1151   DMNetworkComponentHeader header;
1152 
1153   PetscFunctionBegin;
1154   PetscCall(PetscSectionGetOffset(network->plex->localSection, p, &offsetp));
1155   if (compnum == ALL_COMPONENTS) {
1156     *offset = offsetp;
1157     PetscFunctionReturn(0);
1158   }
1159 
1160   PetscCall(PetscSectionGetOffset(network->DataSection, p, &offsetd));
1161   header  = (DMNetworkComponentHeader)(network->componentdataarray + offsetd);
1162   *offset = offsetp + header->offsetvarrel[compnum];
1163   PetscFunctionReturn(0);
1164 }
1165 
1166 /*@
1167   DMNetworkGetGlobalVecOffset - Get the global offset for accessing the variables associated with a component for the given vertex/edge from the global vector
1168 
1169   Not Collective
1170 
1171   Input Parameters:
1172 + dm - the DMNetwork object
1173 . p - the edge or vertex point
1174 - compnum - component number; use ALL_COMPONENTS if no specific component is requested
1175 
1176   Output Parameters:
1177 . offsetg - the global offset
1178 
1179   Notes:
1180     These offsets can be passed to MatSetValues() for matrices obtained with DMCreateMatrix().
1181 
1182     For vectors obtained with DMCreateGlobalVector() the offsets can be used with VecSetValues().
1183 
1184     For vectors obtained with DMCreateGlobalVector() and the array obtained with VecGetArray(vec,&array) you can access or set
1185     the vector values with array[offset - rstart] where restart is obtained with VecGetOwnershipRange(v,&rstart,NULL);
1186 
1187   Level: intermediate
1188 
1189 .seealso: `DMNetworkGetLocalVecOffset()`, `DMGetGlobalVector()`, `DMNetworkGetComponent()`, `DMCreateGlobalVector()`, `VecGetArray()`, `VecSetValues()`, `MatSetValues()`
1190 @*/
1191 PetscErrorCode DMNetworkGetGlobalVecOffset(DM dm, PetscInt p, PetscInt compnum, PetscInt *offsetg)
1192 {
1193   DM_Network              *network = (DM_Network *)dm->data;
1194   PetscInt                 offsetp, offsetd;
1195   DMNetworkComponentHeader header;
1196 
1197   PetscFunctionBegin;
1198   PetscCall(PetscSectionGetOffset(network->plex->globalSection, p, &offsetp));
1199   if (offsetp < 0) offsetp = -(offsetp + 1); /* Convert to actual global offset for ghost vertex */
1200 
1201   if (compnum == ALL_COMPONENTS) {
1202     *offsetg = offsetp;
1203     PetscFunctionReturn(0);
1204   }
1205   PetscCall(PetscSectionGetOffset(network->DataSection, p, &offsetd));
1206   header   = (DMNetworkComponentHeader)(network->componentdataarray + offsetd);
1207   *offsetg = offsetp + header->offsetvarrel[compnum];
1208   PetscFunctionReturn(0);
1209 }
1210 
1211 /*@
1212   DMNetworkGetEdgeOffset - Get the offset for accessing the variables associated with the given edge from the local subvector
1213 
1214   Not Collective
1215 
1216   Input Parameters:
1217 + dm - the DMNetwork object
1218 - p - the edge point
1219 
1220   Output Parameters:
1221 . offset - the offset
1222 
1223   Level: intermediate
1224 
1225 .seealso: `DMNetworkGetLocalVecOffset()`, `DMGetLocalVector()`
1226 @*/
1227 PetscErrorCode DMNetworkGetEdgeOffset(DM dm, PetscInt p, PetscInt *offset)
1228 {
1229   DM_Network *network = (DM_Network *)dm->data;
1230 
1231   PetscFunctionBegin;
1232   PetscCall(PetscSectionGetOffset(network->edge.DofSection, p, offset));
1233   PetscFunctionReturn(0);
1234 }
1235 
1236 /*@
1237   DMNetworkGetVertexOffset - Get the offset for accessing the variables associated with the given vertex from the local subvector
1238 
1239   Not Collective
1240 
1241   Input Parameters:
1242 + dm - the DMNetwork object
1243 - p - the vertex point
1244 
1245   Output Parameters:
1246 . offset - the offset
1247 
1248   Level: intermediate
1249 
1250 .seealso: `DMNetworkGetEdgeOffset()`, `DMGetLocalVector()`
1251 @*/
1252 PetscErrorCode DMNetworkGetVertexOffset(DM dm, PetscInt p, PetscInt *offset)
1253 {
1254   DM_Network *network = (DM_Network *)dm->data;
1255 
1256   PetscFunctionBegin;
1257   p -= network->cloneshared->vStart;
1258   PetscCall(PetscSectionGetOffset(network->vertex.DofSection, p, offset));
1259   PetscFunctionReturn(0);
1260 }
1261 
1262 /*@
1263   DMNetworkAddComponent - Adds a network component and number of variables at the given point (vertex/edge)
1264 
1265   Collective on dm
1266 
1267   Input Parameters:
1268 + dm - the DMNetwork
1269 . p - the vertex/edge point. These points are local indices provided by DMNetworkGetSubnetwork()
1270 . componentkey - component key returned while registering the component with DMNetworkRegisterComponent()
1271 . 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
1272               free this space until after DMSetUp() is called.
1273 - 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
1274 
1275   Notes:
1276     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.
1277 
1278     DMNetworkLayoutSetUp() must be called before this routine.
1279 
1280   Developer Notes:
1281      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.
1282   Level: beginner
1283 
1284 .seealso: `DMNetworkGetComponent()`, `DMNetworkGetSubnetwork()`, `DMNetworkIsGhostVertex()`, `DMNetworkLayoutSetUp()`
1285 @*/
1286 PetscErrorCode DMNetworkAddComponent(DM dm, PetscInt p, PetscInt componentkey, void *compvalue, PetscInt nvar)
1287 {
1288   DM_Network              *network   = (DM_Network *)dm->data;
1289   DMNetworkComponent      *component = &network->component[componentkey];
1290   DMNetworkComponentHeader header;
1291   DMNetworkComponentValue  cvalue;
1292   PetscInt                 compnum;
1293   PetscInt                *compsize, *compkey, *compoffset, *compnvar, *compoffsetvarrel;
1294   void                   **compdata;
1295 
1296   PetscFunctionBegin;
1297   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);
1298   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.");
1299   /* The owning rank and all ghost ranks add nvar */
1300   PetscCall(PetscSectionAddDof(network->DofSection, p, nvar));
1301 
1302   /* The owning rank and all ghost ranks add a component, including compvalue=NULL */
1303   header = &network->header[p];
1304   cvalue = &network->cvalue[p];
1305   if (header->ndata == header->maxcomps) {
1306     PetscInt additional_size;
1307 
1308     /* Reached limit so resize header component arrays */
1309     header->maxcomps += 2;
1310 
1311     /* Allocate arrays for component information and value */
1312     PetscCall(PetscCalloc5(header->maxcomps, &compsize, header->maxcomps, &compkey, header->maxcomps, &compoffset, header->maxcomps, &compnvar, header->maxcomps, &compoffsetvarrel));
1313     PetscCall(PetscMalloc1(header->maxcomps, &compdata));
1314 
1315     /* Recalculate header size */
1316     header->hsize = sizeof(struct _p_DMNetworkComponentHeader) + 5 * header->maxcomps * sizeof(PetscInt);
1317 
1318     header->hsize /= sizeof(DMNetworkComponentGenericDataType);
1319 
1320     /* Copy over component info */
1321     PetscCall(PetscMemcpy(compsize, header->size, header->ndata * sizeof(PetscInt)));
1322     PetscCall(PetscMemcpy(compkey, header->key, header->ndata * sizeof(PetscInt)));
1323     PetscCall(PetscMemcpy(compoffset, header->offset, header->ndata * sizeof(PetscInt)));
1324     PetscCall(PetscMemcpy(compnvar, header->nvar, header->ndata * sizeof(PetscInt)));
1325     PetscCall(PetscMemcpy(compoffsetvarrel, header->offsetvarrel, header->ndata * sizeof(PetscInt)));
1326 
1327     /* Copy over component data pointers */
1328     PetscCall(PetscMemcpy(compdata, cvalue->data, header->ndata * sizeof(void *)));
1329 
1330     /* Free old arrays */
1331     PetscCall(PetscFree5(header->size, header->key, header->offset, header->nvar, header->offsetvarrel));
1332     PetscCall(PetscFree(cvalue->data));
1333 
1334     /* Update pointers */
1335     header->size         = compsize;
1336     header->key          = compkey;
1337     header->offset       = compoffset;
1338     header->nvar         = compnvar;
1339     header->offsetvarrel = compoffsetvarrel;
1340 
1341     cvalue->data = compdata;
1342 
1343     /* Update DataSection Dofs */
1344     /* 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 */
1345     additional_size = (5 * (header->maxcomps - header->ndata) * sizeof(PetscInt)) / sizeof(DMNetworkComponentGenericDataType);
1346     PetscCall(PetscSectionAddDof(network->DataSection, p, additional_size));
1347   }
1348   header = &network->header[p];
1349   cvalue = &network->cvalue[p];
1350 
1351   compnum = header->ndata;
1352 
1353   header->size[compnum] = component->size;
1354   PetscCall(PetscSectionAddDof(network->DataSection, p, component->size));
1355   header->key[compnum] = componentkey;
1356   if (compnum != 0) header->offset[compnum] = header->offset[compnum - 1] + header->size[compnum - 1];
1357   cvalue->data[compnum] = (void *)compvalue;
1358 
1359   /* variables */
1360   header->nvar[compnum] += nvar;
1361   if (compnum != 0) header->offsetvarrel[compnum] = header->offsetvarrel[compnum - 1] + header->nvar[compnum - 1];
1362 
1363   header->ndata++;
1364   PetscFunctionReturn(0);
1365 }
1366 
1367 /*@
1368   DMNetworkGetComponent - Gets the component key, the component data, and the number of variables at a given network point
1369 
1370   Not Collective
1371 
1372   Input Parameters:
1373 + dm - the DMNetwork object
1374 . p - vertex/edge point
1375 - compnum - component number; use ALL_COMPONENTS if sum up all the components
1376 
1377   Output Parameters:
1378 + compkey - the key obtained when registering the component (use NULL if not required)
1379 . component - the component data (use NULL if not required)
1380 - nvar  - number of variables (use NULL if not required)
1381 
1382   Level: beginner
1383 
1384 .seealso: `DMNetworkAddComponent()`, `DMNetworkGetNumComponents()`
1385 @*/
1386 PetscErrorCode DMNetworkGetComponent(DM dm, PetscInt p, PetscInt compnum, PetscInt *compkey, void **component, PetscInt *nvar)
1387 {
1388   DM_Network              *network = (DM_Network *)dm->data;
1389   PetscInt                 offset  = 0;
1390   DMNetworkComponentHeader header;
1391 
1392   PetscFunctionBegin;
1393   if (compnum == ALL_COMPONENTS) {
1394     PetscCall(PetscSectionGetDof(network->DofSection, p, nvar));
1395     PetscFunctionReturn(0);
1396   }
1397 
1398   PetscCall(PetscSectionGetOffset(network->DataSection, p, &offset));
1399   header = (DMNetworkComponentHeader)(network->componentdataarray + offset);
1400 
1401   if (compnum >= 0) {
1402     if (compkey) *compkey = header->key[compnum];
1403     if (component) {
1404       offset += header->hsize + header->offset[compnum];
1405       *component = network->componentdataarray + offset;
1406     }
1407   }
1408 
1409   if (nvar) *nvar = header->nvar[compnum];
1410 
1411   PetscFunctionReturn(0);
1412 }
1413 
1414 /*
1415  Sets up the array that holds the data for all components and its associated section.
1416  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.
1417 */
1418 PetscErrorCode DMNetworkComponentSetUp(DM dm)
1419 {
1420   DM_Network                        *network = (DM_Network *)dm->data;
1421   PetscInt                           arr_size, p, offset, offsetp, ncomp, i, *headerarr;
1422   DMNetworkComponentHeader           header;
1423   DMNetworkComponentValue            cvalue;
1424   DMNetworkComponentHeader           headerinfo;
1425   DMNetworkComponentGenericDataType *componentdataarray;
1426 
1427   PetscFunctionBegin;
1428   PetscCall(PetscSectionSetUp(network->DataSection));
1429   PetscCall(PetscSectionGetStorageSize(network->DataSection, &arr_size));
1430   /* arr_size+1 fixes pipeline test of opensolaris-misc for src/dm/tests/ex10.c -- Do not know why */
1431   PetscCall(PetscCalloc1(arr_size + 1, &network->componentdataarray));
1432   componentdataarray = network->componentdataarray;
1433   for (p = network->cloneshared->pStart; p < network->cloneshared->pEnd; p++) {
1434     PetscCall(PetscSectionGetOffset(network->DataSection, p, &offsetp));
1435     /* Copy header */
1436     header     = &network->header[p];
1437     headerinfo = (DMNetworkComponentHeader)(componentdataarray + offsetp);
1438     PetscCall(PetscMemcpy(headerinfo, header, sizeof(struct _p_DMNetworkComponentHeader)));
1439     headerarr = (PetscInt *)(headerinfo + 1);
1440     PetscCall(PetscMemcpy(headerarr, header->size, header->maxcomps * sizeof(PetscInt)));
1441     headerinfo->size = headerarr;
1442     headerarr += header->maxcomps;
1443     PetscCall(PetscMemcpy(headerarr, header->key, header->maxcomps * sizeof(PetscInt)));
1444     headerinfo->key = headerarr;
1445     headerarr += header->maxcomps;
1446     PetscCall(PetscMemcpy(headerarr, header->offset, header->maxcomps * sizeof(PetscInt)));
1447     headerinfo->offset = headerarr;
1448     headerarr += header->maxcomps;
1449     PetscCall(PetscMemcpy(headerarr, header->nvar, header->maxcomps * sizeof(PetscInt)));
1450     headerinfo->nvar = headerarr;
1451     headerarr += header->maxcomps;
1452     PetscCall(PetscMemcpy(headerarr, header->offsetvarrel, header->maxcomps * sizeof(PetscInt)));
1453     headerinfo->offsetvarrel = headerarr;
1454 
1455     /* Copy data */
1456     cvalue = &network->cvalue[p];
1457     ncomp  = header->ndata;
1458 
1459     for (i = 0; i < ncomp; i++) {
1460       offset = offsetp + header->hsize + header->offset[i];
1461       PetscCall(PetscMemcpy(componentdataarray + offset, cvalue->data[i], header->size[i] * sizeof(DMNetworkComponentGenericDataType)));
1462     }
1463   }
1464 
1465   for (i = network->cloneshared->pStart; i < network->cloneshared->pEnd; i++) {
1466     PetscCall(PetscFree5(network->header[i].size, network->header[i].key, network->header[i].offset, network->header[i].nvar, network->header[i].offsetvarrel));
1467     PetscCall(PetscFree(network->cvalue[i].data));
1468   }
1469   PetscCall(PetscFree2(network->header, network->cvalue));
1470   PetscFunctionReturn(0);
1471 }
1472 
1473 /* Sets up the section for dofs. This routine is called during DMSetUp() */
1474 static PetscErrorCode DMNetworkVariablesSetUp(DM dm)
1475 {
1476   DM_Network *network = (DM_Network *)dm->data;
1477 
1478   PetscFunctionBegin;
1479   PetscCall(PetscSectionSetUp(network->DofSection));
1480   PetscFunctionReturn(0);
1481 }
1482 
1483 /* Get a subsection from a range of points */
1484 static PetscErrorCode DMNetworkGetSubSection_private(PetscSection main, PetscInt pstart, PetscInt pend, PetscSection *subsection)
1485 {
1486   PetscInt i, nvar;
1487 
1488   PetscFunctionBegin;
1489   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)main), subsection));
1490   PetscCall(PetscSectionSetChart(*subsection, 0, pend - pstart));
1491   for (i = pstart; i < pend; i++) {
1492     PetscCall(PetscSectionGetDof(main, i, &nvar));
1493     PetscCall(PetscSectionSetDof(*subsection, i - pstart, nvar));
1494   }
1495 
1496   PetscCall(PetscSectionSetUp(*subsection));
1497   PetscFunctionReturn(0);
1498 }
1499 
1500 /* Create a submap of points with a GlobalToLocal structure */
1501 static PetscErrorCode DMNetworkSetSubMap_private(PetscInt pstart, PetscInt pend, ISLocalToGlobalMapping *map)
1502 {
1503   PetscInt i, *subpoints;
1504 
1505   PetscFunctionBegin;
1506   /* Create index sets to map from "points" to "subpoints" */
1507   PetscCall(PetscMalloc1(pend - pstart, &subpoints));
1508   for (i = pstart; i < pend; i++) subpoints[i - pstart] = i;
1509   PetscCall(ISLocalToGlobalMappingCreate(PETSC_COMM_WORLD, 1, pend - pstart, subpoints, PETSC_COPY_VALUES, map));
1510   PetscCall(PetscFree(subpoints));
1511   PetscFunctionReturn(0);
1512 }
1513 
1514 /*@
1515   DMNetworkAssembleGraphStructures - Assembles vertex and edge data structures. Must be called after DMNetworkDistribute
1516 
1517   Collective on dm
1518 
1519   Input Parameters:
1520 . dm - the DMNetworkObject
1521 
1522   Note: the routine will create alternative orderings for the vertices and edges. Assume global network points are:
1523 
1524   points = [0 1 2 3 4 5 6]
1525 
1526   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]).
1527 
1528   With this new ordering a local PetscSection, global PetscSection and PetscSF will be created specific to the subset.
1529 
1530   Level: intermediate
1531 
1532 @*/
1533 PetscErrorCode DMNetworkAssembleGraphStructures(DM dm)
1534 {
1535   MPI_Comm    comm;
1536   PetscMPIInt size;
1537   DM_Network *network = (DM_Network *)dm->data;
1538 
1539   PetscFunctionBegin;
1540   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1541   PetscCallMPI(MPI_Comm_size(comm, &size));
1542 
1543   /* Create maps for vertices and edges */
1544   PetscCall(DMNetworkSetSubMap_private(network->cloneshared->vStart, network->cloneshared->vEnd, &network->vertex.mapping));
1545   PetscCall(DMNetworkSetSubMap_private(network->cloneshared->eStart, network->cloneshared->eEnd, &network->edge.mapping));
1546 
1547   /* Create local sub-sections */
1548   PetscCall(DMNetworkGetSubSection_private(network->DofSection, network->cloneshared->vStart, network->cloneshared->vEnd, &network->vertex.DofSection));
1549   PetscCall(DMNetworkGetSubSection_private(network->DofSection, network->cloneshared->eStart, network->cloneshared->eEnd, &network->edge.DofSection));
1550 
1551   if (size > 1) {
1552     PetscCall(PetscSFGetSubSF(network->plex->sf, network->vertex.mapping, &network->vertex.sf));
1553 
1554     PetscCall(PetscSectionCreateGlobalSection(network->vertex.DofSection, network->vertex.sf, PETSC_FALSE, PETSC_FALSE, &network->vertex.GlobalDofSection));
1555     PetscCall(PetscSFGetSubSF(network->plex->sf, network->edge.mapping, &network->edge.sf));
1556     PetscCall(PetscSectionCreateGlobalSection(network->edge.DofSection, network->edge.sf, PETSC_FALSE, PETSC_FALSE, &network->edge.GlobalDofSection));
1557   } else {
1558     /* create structures for vertex */
1559     PetscCall(PetscSectionClone(network->vertex.DofSection, &network->vertex.GlobalDofSection));
1560     /* create structures for edge */
1561     PetscCall(PetscSectionClone(network->edge.DofSection, &network->edge.GlobalDofSection));
1562   }
1563 
1564   /* Add viewers */
1565   PetscCall(PetscObjectSetName((PetscObject)network->edge.GlobalDofSection, "Global edge dof section"));
1566   PetscCall(PetscObjectSetName((PetscObject)network->vertex.GlobalDofSection, "Global vertex dof section"));
1567   PetscCall(PetscSectionViewFromOptions(network->edge.GlobalDofSection, NULL, "-edge_global_section_view"));
1568   PetscCall(PetscSectionViewFromOptions(network->vertex.GlobalDofSection, NULL, "-vertex_global_section_view"));
1569   PetscFunctionReturn(0);
1570 }
1571 
1572 /*
1573    Setup a lookup btable for the input v's owning subnetworks
1574    - add all owing subnetworks that connect to this v to the btable
1575      vertex_subnetid = supportingedge_subnetid
1576 */
1577 static inline PetscErrorCode SetSubnetIdLookupBT(DM dm, PetscInt v, PetscInt Nsubnet, PetscBT btable)
1578 {
1579   PetscInt                 e, nedges, offset;
1580   const PetscInt          *edges;
1581   DM_Network              *newDMnetwork = (DM_Network *)dm->data;
1582   DMNetworkComponentHeader header;
1583 
1584   PetscFunctionBegin;
1585   PetscCall(PetscBTMemzero(Nsubnet, btable));
1586   PetscCall(DMNetworkGetSupportingEdges(dm, v, &nedges, &edges));
1587   for (e = 0; e < nedges; e++) {
1588     PetscCall(PetscSectionGetOffset(newDMnetwork->DataSection, edges[e], &offset));
1589     header = (DMNetworkComponentHeader)(newDMnetwork->componentdataarray + offset);
1590     PetscCall(PetscBTSet(btable, header->subnetid));
1591   }
1592   PetscFunctionReturn(0);
1593 }
1594 
1595 /*@
1596   DMNetworkDistribute - Distributes the network and moves associated component data
1597 
1598   Collective
1599 
1600   Input Parameters:
1601 + DM - the DMNetwork object
1602 - overlap - the overlap of partitions, 0 is the default
1603 
1604   Options Database Key:
1605 + -dmnetwork_view - Calls DMView() at the conclusion of DMSetUp()
1606 - -dmnetwork_view_distributed - Calls DMView() at the conclusion of DMNetworkDistribute()
1607 
1608   Notes:
1609   Distributes the network with <overlap>-overlapping partitioning of the edges.
1610 
1611   Level: intermediate
1612 
1613 .seealso: `DMNetworkCreate()`
1614 @*/
1615 PetscErrorCode DMNetworkDistribute(DM *dm, PetscInt overlap)
1616 {
1617   MPI_Comm                 comm;
1618   PetscMPIInt              size;
1619   DM_Network              *oldDMnetwork = (DM_Network *)((*dm)->data);
1620   DM_Network              *newDMnetwork;
1621   PetscSF                  pointsf = NULL;
1622   DM                       newDM;
1623   PetscInt                 j, e, v, offset, *subnetvtx, *subnetedge, Nsubnet, gidx, svtx_idx, nv;
1624   PetscInt                 net, *sv;
1625   PetscBT                  btable;
1626   PetscPartitioner         part;
1627   DMNetworkComponentHeader header;
1628 
1629   PetscFunctionBegin;
1630   PetscValidPointer(dm, 1);
1631   PetscValidHeaderSpecific(*dm, DM_CLASSID, 1);
1632   PetscCall(PetscObjectGetComm((PetscObject)*dm, &comm));
1633   PetscCallMPI(MPI_Comm_size(comm, &size));
1634   if (size == 1) PetscFunctionReturn(0);
1635 
1636   PetscCheck(!overlap, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "overlap %" PetscInt_FMT " != 0 is not supported yet", overlap);
1637 
1638   /* 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. */
1639   PetscCall(PetscLogEventBegin(DMNetwork_Distribute, dm, 0, 0, 0));
1640   PetscCall(DMNetworkCreate(PetscObjectComm((PetscObject)*dm), &newDM));
1641   newDMnetwork                       = (DM_Network *)newDM->data;
1642   newDMnetwork->max_comps_registered = oldDMnetwork->max_comps_registered;
1643   PetscCall(PetscMalloc1(newDMnetwork->max_comps_registered, &newDMnetwork->component));
1644 
1645   /* Enable runtime options for petscpartitioner */
1646   PetscCall(DMPlexGetPartitioner(oldDMnetwork->plex, &part));
1647   PetscCall(PetscPartitionerSetFromOptions(part));
1648 
1649   /* Distribute plex dm */
1650   PetscCall(DMPlexDistribute(oldDMnetwork->plex, overlap, &pointsf, &newDMnetwork->plex));
1651 
1652   /* Distribute dof section */
1653   PetscCall(PetscSectionCreate(comm, &newDMnetwork->DofSection));
1654   PetscCall(PetscSFDistributeSection(pointsf, oldDMnetwork->DofSection, NULL, newDMnetwork->DofSection));
1655 
1656   /* Distribute data and associated section */
1657   PetscCall(PetscSectionCreate(comm, &newDMnetwork->DataSection));
1658   PetscCall(DMPlexDistributeData(newDMnetwork->plex, pointsf, oldDMnetwork->DataSection, MPIU_INT, (void *)oldDMnetwork->componentdataarray, newDMnetwork->DataSection, (void **)&newDMnetwork->componentdataarray));
1659 
1660   PetscCall(PetscSectionGetChart(newDMnetwork->DataSection, &newDMnetwork->cloneshared->pStart, &newDMnetwork->cloneshared->pEnd));
1661   PetscCall(DMPlexGetHeightStratum(newDMnetwork->plex, 0, &newDMnetwork->cloneshared->eStart, &newDMnetwork->cloneshared->eEnd));
1662   PetscCall(DMPlexGetHeightStratum(newDMnetwork->plex, 1, &newDMnetwork->cloneshared->vStart, &newDMnetwork->cloneshared->vEnd));
1663   newDMnetwork->cloneshared->nEdges    = newDMnetwork->cloneshared->eEnd - newDMnetwork->cloneshared->eStart;
1664   newDMnetwork->cloneshared->nVertices = newDMnetwork->cloneshared->vEnd - newDMnetwork->cloneshared->vStart;
1665   newDMnetwork->cloneshared->NVertices = oldDMnetwork->cloneshared->NVertices;
1666   newDMnetwork->cloneshared->NEdges    = oldDMnetwork->cloneshared->NEdges;
1667   newDMnetwork->cloneshared->svtable   = oldDMnetwork->cloneshared->svtable; /* global table! */
1668   oldDMnetwork->cloneshared->svtable   = NULL;
1669 
1670   /* Set Dof section as the section for dm */
1671   PetscCall(DMSetLocalSection(newDMnetwork->plex, newDMnetwork->DofSection));
1672   PetscCall(DMGetGlobalSection(newDMnetwork->plex, &newDMnetwork->GlobalDofSection));
1673 
1674   /* Setup subnetwork info in the newDM */
1675   newDMnetwork->cloneshared->Nsubnet = oldDMnetwork->cloneshared->Nsubnet;
1676   newDMnetwork->cloneshared->Nsvtx   = oldDMnetwork->cloneshared->Nsvtx;
1677   oldDMnetwork->cloneshared->Nsvtx   = 0;
1678   newDMnetwork->cloneshared->svtx    = oldDMnetwork->cloneshared->svtx; /* global vertices! */
1679   oldDMnetwork->cloneshared->svtx    = NULL;
1680   PetscCall(PetscCalloc1(newDMnetwork->cloneshared->Nsubnet, &newDMnetwork->cloneshared->subnet));
1681 
1682   /* Copy over the global number of vertices and edges in each subnetwork.
1683      Note: these are calculated in DMNetworkLayoutSetUp()
1684   */
1685   Nsubnet = newDMnetwork->cloneshared->Nsubnet;
1686   for (j = 0; j < Nsubnet; j++) {
1687     newDMnetwork->cloneshared->subnet[j].Nvtx  = oldDMnetwork->cloneshared->subnet[j].Nvtx;
1688     newDMnetwork->cloneshared->subnet[j].Nedge = oldDMnetwork->cloneshared->subnet[j].Nedge;
1689   }
1690 
1691   /* Count local nedges for subnetworks */
1692   for (e = newDMnetwork->cloneshared->eStart; e < newDMnetwork->cloneshared->eEnd; e++) {
1693     PetscCall(PetscSectionGetOffset(newDMnetwork->DataSection, e, &offset));
1694     header = (DMNetworkComponentHeader)(newDMnetwork->componentdataarray + offset);
1695 
1696     /* Update pointers */
1697     header->size         = (PetscInt *)(header + 1);
1698     header->key          = header->size + header->maxcomps;
1699     header->offset       = header->key + header->maxcomps;
1700     header->nvar         = header->offset + header->maxcomps;
1701     header->offsetvarrel = header->nvar + header->maxcomps;
1702 
1703     newDMnetwork->cloneshared->subnet[header->subnetid].nedge++;
1704   }
1705 
1706   /* Setup a btable to keep track subnetworks owned by this process at a shared vertex */
1707   if (newDMnetwork->cloneshared->Nsvtx) PetscCall(PetscBTCreate(Nsubnet, &btable));
1708 
1709   /* Count local nvtx for subnetworks */
1710   for (v = newDMnetwork->cloneshared->vStart; v < newDMnetwork->cloneshared->vEnd; v++) {
1711     PetscCall(PetscSectionGetOffset(newDMnetwork->DataSection, v, &offset));
1712     header = (DMNetworkComponentHeader)(newDMnetwork->componentdataarray + offset);
1713 
1714     /* Update pointers */
1715     header->size         = (PetscInt *)(header + 1);
1716     header->key          = header->size + header->maxcomps;
1717     header->offset       = header->key + header->maxcomps;
1718     header->nvar         = header->offset + header->maxcomps;
1719     header->offsetvarrel = header->nvar + header->maxcomps;
1720 
1721     /* shared vertices: use gidx=header->index to check if v is a shared vertex */
1722     gidx = header->index;
1723     PetscCall(PetscTableFind(newDMnetwork->cloneshared->svtable, gidx + 1, &svtx_idx));
1724     svtx_idx--;
1725 
1726     if (svtx_idx < 0) { /* not a shared vertex */
1727       newDMnetwork->cloneshared->subnet[header->subnetid].nvtx++;
1728     } else { /* a shared vertex belongs to more than one subnetworks, it is being counted by multiple subnets */
1729       /* Setup a lookup btable for this v's owning subnetworks */
1730       PetscCall(SetSubnetIdLookupBT(newDM, v, Nsubnet, btable));
1731 
1732       for (j = 0; j < newDMnetwork->cloneshared->svtx[svtx_idx].n; j++) {
1733         sv  = newDMnetwork->cloneshared->svtx[svtx_idx].sv + 2 * j;
1734         net = sv[0];
1735         if (PetscBTLookup(btable, net)) newDMnetwork->cloneshared->subnet[net].nvtx++; /* sv is on net owned by this process */
1736       }
1737     }
1738   }
1739 
1740   /* Get total local nvtx for subnetworks */
1741   nv = 0;
1742   for (j = 0; j < Nsubnet; j++) nv += newDMnetwork->cloneshared->subnet[j].nvtx;
1743   nv += newDMnetwork->cloneshared->Nsvtx;
1744 
1745   /* Now create the vertices and edge arrays for the subnetworks */
1746   PetscCall(PetscCalloc2(newDMnetwork->cloneshared->nEdges, &subnetedge, nv, &subnetvtx)); /* Maps local vertex to local subnetwork's vertex */
1747   newDMnetwork->cloneshared->subnetedge = subnetedge;
1748   newDMnetwork->cloneshared->subnetvtx  = subnetvtx;
1749   for (j = 0; j < newDMnetwork->cloneshared->Nsubnet; j++) {
1750     newDMnetwork->cloneshared->subnet[j].edges = subnetedge;
1751     subnetedge += newDMnetwork->cloneshared->subnet[j].nedge;
1752 
1753     newDMnetwork->cloneshared->subnet[j].vertices = subnetvtx;
1754     subnetvtx += newDMnetwork->cloneshared->subnet[j].nvtx;
1755 
1756     /* 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. */
1757     newDMnetwork->cloneshared->subnet[j].nvtx = newDMnetwork->cloneshared->subnet[j].nedge = 0;
1758   }
1759   newDMnetwork->cloneshared->svertices = subnetvtx;
1760 
1761   /* Set the edges and vertices in each subnetwork */
1762   for (e = newDMnetwork->cloneshared->eStart; e < newDMnetwork->cloneshared->eEnd; e++) {
1763     PetscCall(PetscSectionGetOffset(newDMnetwork->DataSection, e, &offset));
1764     header                                                                                                                 = (DMNetworkComponentHeader)(newDMnetwork->componentdataarray + offset);
1765     newDMnetwork->cloneshared->subnet[header->subnetid].edges[newDMnetwork->cloneshared->subnet[header->subnetid].nedge++] = e;
1766   }
1767 
1768   nv = 0;
1769   for (v = newDMnetwork->cloneshared->vStart; v < newDMnetwork->cloneshared->vEnd; v++) {
1770     PetscCall(PetscSectionGetOffset(newDMnetwork->DataSection, v, &offset));
1771     header = (DMNetworkComponentHeader)(newDMnetwork->componentdataarray + offset);
1772 
1773     /* coupling vertices: use gidx = header->index to check if v is a coupling vertex */
1774     PetscCall(PetscTableFind(newDMnetwork->cloneshared->svtable, header->index + 1, &svtx_idx));
1775     svtx_idx--;
1776     if (svtx_idx < 0) {
1777       newDMnetwork->cloneshared->subnet[header->subnetid].vertices[newDMnetwork->cloneshared->subnet[header->subnetid].nvtx++] = v;
1778     } else { /* a shared vertex */
1779       newDMnetwork->cloneshared->svertices[nv++] = v;
1780 
1781       /* Setup a lookup btable for this v's owning subnetworks */
1782       PetscCall(SetSubnetIdLookupBT(newDM, v, Nsubnet, btable));
1783 
1784       for (j = 0; j < newDMnetwork->cloneshared->svtx[svtx_idx].n; j++) {
1785         sv  = newDMnetwork->cloneshared->svtx[svtx_idx].sv + 2 * j;
1786         net = sv[0];
1787         if (PetscBTLookup(btable, net)) newDMnetwork->cloneshared->subnet[net].vertices[newDMnetwork->cloneshared->subnet[net].nvtx++] = v;
1788       }
1789     }
1790   }
1791   newDMnetwork->cloneshared->nsvtx = nv; /* num of local shared vertices */
1792 
1793   newDM->setupcalled                          = (*dm)->setupcalled;
1794   newDMnetwork->cloneshared->distributecalled = PETSC_TRUE;
1795 
1796   /* Free spaces */
1797   PetscCall(PetscSFDestroy(&pointsf));
1798   PetscCall(DMDestroy(dm));
1799   if (newDMnetwork->cloneshared->Nsvtx) PetscCall(PetscBTDestroy(&btable));
1800 
1801   /* View distributed dmnetwork */
1802   PetscCall(DMViewFromOptions(newDM, NULL, "-dmnetwork_view_distributed"));
1803 
1804   *dm = newDM;
1805   PetscCall(PetscLogEventEnd(DMNetwork_Distribute, dm, 0, 0, 0));
1806   PetscFunctionReturn(0);
1807 }
1808 
1809 /*@C
1810   PetscSFGetSubSF - Returns an SF for a specific subset of points. Leaves are re-numbered to reflect the new ordering
1811 
1812  Collective
1813 
1814   Input Parameters:
1815 + mainSF - the original SF structure
1816 - map - a ISLocalToGlobal mapping that contains the subset of points
1817 
1818   Output Parameter:
1819 . subSF - a subset of the mainSF for the desired subset.
1820 
1821   Level: intermediate
1822 @*/
1823 PetscErrorCode PetscSFGetSubSF(PetscSF mainsf, ISLocalToGlobalMapping map, PetscSF *subSF)
1824 {
1825   PetscInt           nroots, nleaves, *ilocal_sub;
1826   PetscInt           i, *ilocal_map, nroots_sub, nleaves_sub = 0;
1827   PetscInt          *local_points, *remote_points;
1828   PetscSFNode       *iremote_sub;
1829   const PetscInt    *ilocal;
1830   const PetscSFNode *iremote;
1831 
1832   PetscFunctionBegin;
1833   PetscCall(PetscSFGetGraph(mainsf, &nroots, &nleaves, &ilocal, &iremote));
1834 
1835   /* Look for leaves that pertain to the subset of points. Get the local ordering */
1836   PetscCall(PetscMalloc1(nleaves, &ilocal_map));
1837   PetscCall(ISGlobalToLocalMappingApply(map, IS_GTOLM_MASK, nleaves, ilocal, NULL, ilocal_map));
1838   for (i = 0; i < nleaves; i++) {
1839     if (ilocal_map[i] != -1) nleaves_sub += 1;
1840   }
1841   /* Re-number ilocal with subset numbering. Need information from roots */
1842   PetscCall(PetscMalloc2(nroots, &local_points, nroots, &remote_points));
1843   for (i = 0; i < nroots; i++) local_points[i] = i;
1844   PetscCall(ISGlobalToLocalMappingApply(map, IS_GTOLM_MASK, nroots, local_points, NULL, local_points));
1845   PetscCall(PetscSFBcastBegin(mainsf, MPIU_INT, local_points, remote_points, MPI_REPLACE));
1846   PetscCall(PetscSFBcastEnd(mainsf, MPIU_INT, local_points, remote_points, MPI_REPLACE));
1847   /* Fill up graph using local (that is, local to the subset) numbering. */
1848   PetscCall(PetscMalloc1(nleaves_sub, &ilocal_sub));
1849   PetscCall(PetscMalloc1(nleaves_sub, &iremote_sub));
1850   nleaves_sub = 0;
1851   for (i = 0; i < nleaves; i++) {
1852     if (ilocal_map[i] != -1) {
1853       ilocal_sub[nleaves_sub]        = ilocal_map[i];
1854       iremote_sub[nleaves_sub].rank  = iremote[i].rank;
1855       iremote_sub[nleaves_sub].index = remote_points[ilocal[i]];
1856       nleaves_sub += 1;
1857     }
1858   }
1859   PetscCall(PetscFree2(local_points, remote_points));
1860   PetscCall(ISLocalToGlobalMappingGetSize(map, &nroots_sub));
1861 
1862   /* Create new subSF */
1863   PetscCall(PetscSFCreate(PETSC_COMM_WORLD, subSF));
1864   PetscCall(PetscSFSetFromOptions(*subSF));
1865   PetscCall(PetscSFSetGraph(*subSF, nroots_sub, nleaves_sub, ilocal_sub, PETSC_OWN_POINTER, iremote_sub, PETSC_COPY_VALUES));
1866   PetscCall(PetscFree(ilocal_map));
1867   PetscCall(PetscFree(iremote_sub));
1868   PetscFunctionReturn(0);
1869 }
1870 
1871 /*@C
1872   DMNetworkGetSupportingEdges - Return the supporting edges for this vertex point
1873 
1874   Not Collective
1875 
1876   Input Parameters:
1877 + dm - the DMNetwork object
1878 - p  - the vertex point
1879 
1880   Output Parameters:
1881 + nedges - number of edges connected to this vertex point
1882 - edges  - list of edge points
1883 
1884   Level: beginner
1885 
1886   Fortran Notes:
1887   Since it returns an array, this routine is only available in Fortran 90, and you must
1888   include petsc.h90 in your code.
1889 
1890 .seealso: `DMNetworkCreate()`, `DMNetworkGetConnectedVertices()`
1891 @*/
1892 PetscErrorCode DMNetworkGetSupportingEdges(DM dm, PetscInt vertex, PetscInt *nedges, const PetscInt *edges[])
1893 {
1894   DM_Network *network = (DM_Network *)dm->data;
1895 
1896   PetscFunctionBegin;
1897   PetscCall(DMPlexGetSupportSize(network->plex, vertex, nedges));
1898   if (edges) PetscCall(DMPlexGetSupport(network->plex, vertex, edges));
1899   PetscFunctionReturn(0);
1900 }
1901 
1902 /*@C
1903   DMNetworkGetConnectedVertices - Return the connected vertices for this edge point
1904 
1905   Not Collective
1906 
1907   Input Parameters:
1908 + dm - the DMNetwork object
1909 - p - the edge point
1910 
1911   Output Parameters:
1912 . vertices - vertices connected to this edge
1913 
1914   Level: beginner
1915 
1916   Fortran Notes:
1917   Since it returns an array, this routine is only available in Fortran 90, and you must
1918   include petsc.h90 in your code.
1919 
1920 .seealso: `DMNetworkCreate()`, `DMNetworkGetSupportingEdges()`
1921 @*/
1922 PetscErrorCode DMNetworkGetConnectedVertices(DM dm, PetscInt edge, const PetscInt *vertices[])
1923 {
1924   DM_Network *network = (DM_Network *)dm->data;
1925 
1926   PetscFunctionBegin;
1927   PetscCall(DMPlexGetCone(network->plex, edge, vertices));
1928   PetscFunctionReturn(0);
1929 }
1930 
1931 /*@
1932   DMNetworkIsSharedVertex - Returns TRUE if the vertex is shared by subnetworks
1933 
1934   Not Collective
1935 
1936   Input Parameters:
1937 + dm - the DMNetwork object
1938 - p - the vertex point
1939 
1940   Output Parameter:
1941 . flag - TRUE if the vertex is shared by subnetworks
1942 
1943   Level: beginner
1944 
1945 .seealso: `DMNetworkAddSharedVertices()`, `DMNetworkIsGhostVertex()`
1946 @*/
1947 PetscErrorCode DMNetworkIsSharedVertex(DM dm, PetscInt p, PetscBool *flag)
1948 {
1949   PetscInt i;
1950 
1951   PetscFunctionBegin;
1952   *flag = PETSC_FALSE;
1953 
1954   if (dm->setupcalled) { /* DMNetworkGetGlobalVertexIndex() requires DMSetUp() be called */
1955     DM_Network *network = (DM_Network *)dm->data;
1956     PetscInt    gidx;
1957     PetscCall(DMNetworkGetGlobalVertexIndex(dm, p, &gidx));
1958     PetscCall(PetscTableFind(network->cloneshared->svtable, gidx + 1, &i));
1959     if (i) *flag = PETSC_TRUE;
1960   } else { /* would be removed? */
1961     PetscInt        nv;
1962     const PetscInt *vtx;
1963     PetscCall(DMNetworkGetSharedVertices(dm, &nv, &vtx));
1964     for (i = 0; i < nv; i++) {
1965       if (p == vtx[i]) {
1966         *flag = PETSC_TRUE;
1967         break;
1968       }
1969     }
1970   }
1971   PetscFunctionReturn(0);
1972 }
1973 
1974 /*@
1975   DMNetworkIsGhostVertex - Returns TRUE if the vertex is a ghost vertex
1976 
1977   Not Collective
1978 
1979   Input Parameters:
1980 + dm - the DMNetwork object
1981 - p - the vertex point
1982 
1983   Output Parameter:
1984 . isghost - TRUE if the vertex is a ghost point
1985 
1986   Level: beginner
1987 
1988 .seealso: `DMNetworkGetConnectedVertices()`, `DMNetworkGetVertexRange()`, `DMNetworkIsSharedVertex()`
1989 @*/
1990 PetscErrorCode DMNetworkIsGhostVertex(DM dm, PetscInt p, PetscBool *isghost)
1991 {
1992   DM_Network  *network = (DM_Network *)dm->data;
1993   PetscInt     offsetg;
1994   PetscSection sectiong;
1995 
1996   PetscFunctionBegin;
1997   *isghost = PETSC_FALSE;
1998   PetscCall(DMGetGlobalSection(network->plex, &sectiong));
1999   PetscCall(PetscSectionGetOffset(sectiong, p, &offsetg));
2000   if (offsetg < 0) *isghost = PETSC_TRUE;
2001   PetscFunctionReturn(0);
2002 }
2003 
2004 PetscErrorCode DMSetUp_Network(DM dm)
2005 {
2006   PetscFunctionBegin;
2007   PetscCall(PetscLogEventBegin(DMNetwork_SetUpNetwork, dm, 0, 0, 0));
2008   PetscCall(DMNetworkFinalizeComponents(dm));
2009   /* View dmnetwork */
2010   PetscCall(DMViewFromOptions(dm, NULL, "-dmnetwork_view"));
2011   PetscCall(PetscLogEventEnd(DMNetwork_SetUpNetwork, dm, 0, 0, 0));
2012   PetscFunctionReturn(0);
2013 }
2014 
2015 /*@
2016   DMNetworkHasJacobian - Sets global flag for using user's sub Jacobian matrices
2017       -- replaced by DMNetworkSetOption(network,userjacobian,PETSC_TURE)?
2018 
2019   Collective
2020 
2021   Input Parameters:
2022 + dm - the DMNetwork object
2023 . eflg - turn the option on (PETSC_TRUE) or off (PETSC_FALSE) if user provides Jacobian for edges
2024 - vflg - turn the option on (PETSC_TRUE) or off (PETSC_FALSE) if user provides Jacobian for vertices
2025 
2026  Level: intermediate
2027 
2028 @*/
2029 PetscErrorCode DMNetworkHasJacobian(DM dm, PetscBool eflg, PetscBool vflg)
2030 {
2031   DM_Network *network   = (DM_Network *)dm->data;
2032   PetscInt    nVertices = network->cloneshared->nVertices;
2033 
2034   PetscFunctionBegin;
2035   network->userEdgeJacobian   = eflg;
2036   network->userVertexJacobian = vflg;
2037 
2038   if (eflg && !network->Je) PetscCall(PetscCalloc1(3 * network->cloneshared->nEdges, &network->Je));
2039 
2040   if (vflg && !network->Jv && nVertices) {
2041     PetscInt        i, *vptr, nedges, vStart = network->cloneshared->vStart;
2042     PetscInt        nedges_total;
2043     const PetscInt *edges;
2044 
2045     /* count nvertex_total */
2046     nedges_total = 0;
2047     PetscCall(PetscMalloc1(nVertices + 1, &vptr));
2048 
2049     vptr[0] = 0;
2050     for (i = 0; i < nVertices; i++) {
2051       PetscCall(DMNetworkGetSupportingEdges(dm, i + vStart, &nedges, &edges));
2052       nedges_total += nedges;
2053       vptr[i + 1] = vptr[i] + 2 * nedges + 1;
2054     }
2055 
2056     PetscCall(PetscCalloc1(2 * nedges_total + nVertices, &network->Jv));
2057     network->Jvptr = vptr;
2058   }
2059   PetscFunctionReturn(0);
2060 }
2061 
2062 /*@
2063   DMNetworkEdgeSetMatrix - Sets user-provided Jacobian matrices for this edge to the network
2064 
2065   Not Collective
2066 
2067   Input Parameters:
2068 + dm - the DMNetwork object
2069 . p - the edge point
2070 - J - array (size = 3) of Jacobian submatrices for this edge point:
2071         J[0]: this edge
2072         J[1] and J[2]: connected vertices, obtained by calling DMNetworkGetConnectedVertices()
2073 
2074   Level: advanced
2075 
2076 .seealso: `DMNetworkVertexSetMatrix()`
2077 @*/
2078 PetscErrorCode DMNetworkEdgeSetMatrix(DM dm, PetscInt p, Mat J[])
2079 {
2080   DM_Network *network = (DM_Network *)dm->data;
2081 
2082   PetscFunctionBegin;
2083   PetscCheck(network->Je, PetscObjectComm((PetscObject)dm), PETSC_ERR_ORDER, "Must call DMNetworkHasJacobian() collectively before calling DMNetworkEdgeSetMatrix");
2084 
2085   if (J) {
2086     network->Je[3 * p]     = J[0];
2087     network->Je[3 * p + 1] = J[1];
2088     network->Je[3 * p + 2] = J[2];
2089   }
2090   PetscFunctionReturn(0);
2091 }
2092 
2093 /*@
2094   DMNetworkVertexSetMatrix - Sets user-provided Jacobian matrix for this vertex to the network
2095 
2096   Not Collective
2097 
2098   Input Parameters:
2099 + dm - The DMNetwork object
2100 . p - the vertex point
2101 - J - array of Jacobian (size = 2*(num of supporting edges) + 1) submatrices for this vertex point:
2102         J[0]:       this vertex
2103         J[1+2*i]:   i-th supporting edge
2104         J[1+2*i+1]: i-th connected vertex
2105 
2106   Level: advanced
2107 
2108 .seealso: `DMNetworkEdgeSetMatrix()`
2109 @*/
2110 PetscErrorCode DMNetworkVertexSetMatrix(DM dm, PetscInt p, Mat J[])
2111 {
2112   DM_Network     *network = (DM_Network *)dm->data;
2113   PetscInt        i, *vptr, nedges, vStart = network->cloneshared->vStart;
2114   const PetscInt *edges;
2115 
2116   PetscFunctionBegin;
2117   PetscCheck(network->Jv, PetscObjectComm((PetscObject)dm), PETSC_ERR_ORDER, "Must call DMNetworkHasJacobian() collectively before calling DMNetworkVertexSetMatrix");
2118 
2119   if (J) {
2120     vptr                          = network->Jvptr;
2121     network->Jv[vptr[p - vStart]] = J[0]; /* Set Jacobian for this vertex */
2122 
2123     /* Set Jacobian for each supporting edge and connected vertex */
2124     PetscCall(DMNetworkGetSupportingEdges(dm, p, &nedges, &edges));
2125     for (i = 1; i <= 2 * nedges; i++) network->Jv[vptr[p - vStart] + i] = J[i];
2126   }
2127   PetscFunctionReturn(0);
2128 }
2129 
2130 static inline PetscErrorCode MatSetPreallocationDenseblock_private(PetscInt nrows, PetscInt *rows, PetscInt ncols, PetscBool ghost, Vec vdnz, Vec vonz)
2131 {
2132   PetscInt    j;
2133   PetscScalar val = (PetscScalar)ncols;
2134 
2135   PetscFunctionBegin;
2136   if (!ghost) {
2137     for (j = 0; j < nrows; j++) PetscCall(VecSetValues(vdnz, 1, &rows[j], &val, ADD_VALUES));
2138   } else {
2139     for (j = 0; j < nrows; j++) PetscCall(VecSetValues(vonz, 1, &rows[j], &val, ADD_VALUES));
2140   }
2141   PetscFunctionReturn(0);
2142 }
2143 
2144 static inline PetscErrorCode MatSetPreallocationUserblock_private(Mat Ju, PetscInt nrows, PetscInt *rows, PetscInt ncols, PetscBool ghost, Vec vdnz, Vec vonz)
2145 {
2146   PetscInt    j, ncols_u;
2147   PetscScalar val;
2148 
2149   PetscFunctionBegin;
2150   if (!ghost) {
2151     for (j = 0; j < nrows; j++) {
2152       PetscCall(MatGetRow(Ju, j, &ncols_u, NULL, NULL));
2153       val = (PetscScalar)ncols_u;
2154       PetscCall(VecSetValues(vdnz, 1, &rows[j], &val, ADD_VALUES));
2155       PetscCall(MatRestoreRow(Ju, j, &ncols_u, NULL, NULL));
2156     }
2157   } else {
2158     for (j = 0; j < nrows; j++) {
2159       PetscCall(MatGetRow(Ju, j, &ncols_u, NULL, NULL));
2160       val = (PetscScalar)ncols_u;
2161       PetscCall(VecSetValues(vonz, 1, &rows[j], &val, ADD_VALUES));
2162       PetscCall(MatRestoreRow(Ju, j, &ncols_u, NULL, NULL));
2163     }
2164   }
2165   PetscFunctionReturn(0);
2166 }
2167 
2168 static inline PetscErrorCode MatSetPreallocationblock_private(Mat Ju, PetscInt nrows, PetscInt *rows, PetscInt ncols, PetscBool ghost, Vec vdnz, Vec vonz)
2169 {
2170   PetscFunctionBegin;
2171   if (Ju) {
2172     PetscCall(MatSetPreallocationUserblock_private(Ju, nrows, rows, ncols, ghost, vdnz, vonz));
2173   } else {
2174     PetscCall(MatSetPreallocationDenseblock_private(nrows, rows, ncols, ghost, vdnz, vonz));
2175   }
2176   PetscFunctionReturn(0);
2177 }
2178 
2179 static inline PetscErrorCode MatSetDenseblock_private(PetscInt nrows, PetscInt *rows, PetscInt ncols, PetscInt cstart, Mat *J)
2180 {
2181   PetscInt     j, *cols;
2182   PetscScalar *zeros;
2183 
2184   PetscFunctionBegin;
2185   PetscCall(PetscCalloc2(ncols, &cols, nrows * ncols, &zeros));
2186   for (j = 0; j < ncols; j++) cols[j] = j + cstart;
2187   PetscCall(MatSetValues(*J, nrows, rows, ncols, cols, zeros, INSERT_VALUES));
2188   PetscCall(PetscFree2(cols, zeros));
2189   PetscFunctionReturn(0);
2190 }
2191 
2192 static inline PetscErrorCode MatSetUserblock_private(Mat Ju, PetscInt nrows, PetscInt *rows, PetscInt ncols, PetscInt cstart, Mat *J)
2193 {
2194   PetscInt        j, M, N, row, col, ncols_u;
2195   const PetscInt *cols;
2196   PetscScalar     zero = 0.0;
2197 
2198   PetscFunctionBegin;
2199   PetscCall(MatGetSize(Ju, &M, &N));
2200   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);
2201 
2202   for (row = 0; row < nrows; row++) {
2203     PetscCall(MatGetRow(Ju, row, &ncols_u, &cols, NULL));
2204     for (j = 0; j < ncols_u; j++) {
2205       col = cols[j] + cstart;
2206       PetscCall(MatSetValues(*J, 1, &rows[row], 1, &col, &zero, INSERT_VALUES));
2207     }
2208     PetscCall(MatRestoreRow(Ju, row, &ncols_u, &cols, NULL));
2209   }
2210   PetscFunctionReturn(0);
2211 }
2212 
2213 static inline PetscErrorCode MatSetblock_private(Mat Ju, PetscInt nrows, PetscInt *rows, PetscInt ncols, PetscInt cstart, Mat *J)
2214 {
2215   PetscFunctionBegin;
2216   if (Ju) {
2217     PetscCall(MatSetUserblock_private(Ju, nrows, rows, ncols, cstart, J));
2218   } else {
2219     PetscCall(MatSetDenseblock_private(nrows, rows, ncols, cstart, J));
2220   }
2221   PetscFunctionReturn(0);
2222 }
2223 
2224 /* 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.
2225 */
2226 PetscErrorCode CreateSubGlobalToLocalMapping_private(PetscSection globalsec, PetscSection localsec, ISLocalToGlobalMapping *ltog)
2227 {
2228   PetscInt  i, size, dof;
2229   PetscInt *glob2loc;
2230 
2231   PetscFunctionBegin;
2232   PetscCall(PetscSectionGetStorageSize(localsec, &size));
2233   PetscCall(PetscMalloc1(size, &glob2loc));
2234 
2235   for (i = 0; i < size; i++) {
2236     PetscCall(PetscSectionGetOffset(globalsec, i, &dof));
2237     dof         = (dof >= 0) ? dof : -(dof + 1);
2238     glob2loc[i] = dof;
2239   }
2240 
2241   PetscCall(ISLocalToGlobalMappingCreate(PETSC_COMM_WORLD, 1, size, glob2loc, PETSC_OWN_POINTER, ltog));
2242 #if 0
2243   PetscCall(PetscIntView(size,glob2loc,PETSC_VIEWER_STDOUT_WORLD));
2244 #endif
2245   PetscFunctionReturn(0);
2246 }
2247 
2248 #include <petsc/private/matimpl.h>
2249 
2250 PetscErrorCode DMCreateMatrix_Network_Nest(DM dm, Mat *J)
2251 {
2252   DM_Network            *network = (DM_Network *)dm->data;
2253   PetscInt               eDof, vDof;
2254   Mat                    j11, j12, j21, j22, bA[2][2];
2255   MPI_Comm               comm;
2256   ISLocalToGlobalMapping eISMap, vISMap;
2257 
2258   PetscFunctionBegin;
2259   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
2260 
2261   PetscCall(PetscSectionGetConstrainedStorageSize(network->edge.GlobalDofSection, &eDof));
2262   PetscCall(PetscSectionGetConstrainedStorageSize(network->vertex.GlobalDofSection, &vDof));
2263 
2264   PetscCall(MatCreate(comm, &j11));
2265   PetscCall(MatSetSizes(j11, eDof, eDof, PETSC_DETERMINE, PETSC_DETERMINE));
2266   PetscCall(MatSetType(j11, MATMPIAIJ));
2267 
2268   PetscCall(MatCreate(comm, &j12));
2269   PetscCall(MatSetSizes(j12, eDof, vDof, PETSC_DETERMINE, PETSC_DETERMINE));
2270   PetscCall(MatSetType(j12, MATMPIAIJ));
2271 
2272   PetscCall(MatCreate(comm, &j21));
2273   PetscCall(MatSetSizes(j21, vDof, eDof, PETSC_DETERMINE, PETSC_DETERMINE));
2274   PetscCall(MatSetType(j21, MATMPIAIJ));
2275 
2276   PetscCall(MatCreate(comm, &j22));
2277   PetscCall(MatSetSizes(j22, vDof, vDof, PETSC_DETERMINE, PETSC_DETERMINE));
2278   PetscCall(MatSetType(j22, MATMPIAIJ));
2279 
2280   bA[0][0] = j11;
2281   bA[0][1] = j12;
2282   bA[1][0] = j21;
2283   bA[1][1] = j22;
2284 
2285   PetscCall(CreateSubGlobalToLocalMapping_private(network->edge.GlobalDofSection, network->edge.DofSection, &eISMap));
2286   PetscCall(CreateSubGlobalToLocalMapping_private(network->vertex.GlobalDofSection, network->vertex.DofSection, &vISMap));
2287 
2288   PetscCall(MatSetLocalToGlobalMapping(j11, eISMap, eISMap));
2289   PetscCall(MatSetLocalToGlobalMapping(j12, eISMap, vISMap));
2290   PetscCall(MatSetLocalToGlobalMapping(j21, vISMap, eISMap));
2291   PetscCall(MatSetLocalToGlobalMapping(j22, vISMap, vISMap));
2292 
2293   PetscCall(MatSetUp(j11));
2294   PetscCall(MatSetUp(j12));
2295   PetscCall(MatSetUp(j21));
2296   PetscCall(MatSetUp(j22));
2297 
2298   PetscCall(MatCreateNest(comm, 2, NULL, 2, NULL, &bA[0][0], J));
2299   PetscCall(MatSetUp(*J));
2300   PetscCall(MatNestSetVecType(*J, VECNEST));
2301   PetscCall(MatDestroy(&j11));
2302   PetscCall(MatDestroy(&j12));
2303   PetscCall(MatDestroy(&j21));
2304   PetscCall(MatDestroy(&j22));
2305 
2306   PetscCall(MatAssemblyBegin(*J, MAT_FINAL_ASSEMBLY));
2307   PetscCall(MatAssemblyEnd(*J, MAT_FINAL_ASSEMBLY));
2308   PetscCall(MatSetOption(*J, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE));
2309 
2310   /* Free structures */
2311   PetscCall(ISLocalToGlobalMappingDestroy(&eISMap));
2312   PetscCall(ISLocalToGlobalMappingDestroy(&vISMap));
2313   PetscFunctionReturn(0);
2314 }
2315 
2316 PetscErrorCode DMCreateMatrix_Network(DM dm, Mat *J)
2317 {
2318   DM_Network     *network = (DM_Network *)dm->data;
2319   PetscInt        eStart, eEnd, vStart, vEnd, rstart, nrows, *rows, localSize;
2320   PetscInt        cstart, ncols, j, e, v;
2321   PetscBool       ghost, ghost_vc, ghost2, isNest;
2322   Mat             Juser;
2323   PetscSection    sectionGlobal;
2324   PetscInt        nedges, *vptr = NULL, vc, *rows_v; /* suppress maybe-uninitialized warning */
2325   const PetscInt *edges, *cone;
2326   MPI_Comm        comm;
2327   MatType         mtype;
2328   Vec             vd_nz, vo_nz;
2329   PetscInt       *dnnz, *onnz;
2330   PetscScalar    *vdnz, *vonz;
2331 
2332   PetscFunctionBegin;
2333   mtype = dm->mattype;
2334   PetscCall(PetscStrcmp(mtype, MATNEST, &isNest));
2335   if (isNest) {
2336     PetscCall(DMCreateMatrix_Network_Nest(dm, J));
2337     PetscCall(MatSetDM(*J, dm));
2338     PetscFunctionReturn(0);
2339   }
2340 
2341   if (!network->userEdgeJacobian && !network->userVertexJacobian) {
2342     /* user does not provide Jacobian blocks */
2343     PetscCall(DMCreateMatrix_Plex(network->plex, J));
2344     PetscCall(MatSetDM(*J, dm));
2345     PetscFunctionReturn(0);
2346   }
2347 
2348   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2349   PetscCall(DMGetGlobalSection(network->plex, &sectionGlobal));
2350   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2351   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2352 
2353   PetscCall(MatSetType(*J, MATAIJ));
2354   PetscCall(MatSetFromOptions(*J));
2355 
2356   /* (1) Set matrix preallocation */
2357   /*------------------------------*/
2358   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
2359   PetscCall(VecCreate(comm, &vd_nz));
2360   PetscCall(VecSetSizes(vd_nz, localSize, PETSC_DECIDE));
2361   PetscCall(VecSetFromOptions(vd_nz));
2362   PetscCall(VecSet(vd_nz, 0.0));
2363   PetscCall(VecDuplicate(vd_nz, &vo_nz));
2364 
2365   /* Set preallocation for edges */
2366   /*-----------------------------*/
2367   PetscCall(DMNetworkGetEdgeRange(dm, &eStart, &eEnd));
2368 
2369   PetscCall(PetscMalloc1(localSize, &rows));
2370   for (e = eStart; e < eEnd; e++) {
2371     /* Get row indices */
2372     PetscCall(DMNetworkGetGlobalVecOffset(dm, e, ALL_COMPONENTS, &rstart));
2373     PetscCall(PetscSectionGetDof(network->DofSection, e, &nrows));
2374     if (nrows) {
2375       for (j = 0; j < nrows; j++) rows[j] = j + rstart;
2376 
2377       /* Set preallocation for connected vertices */
2378       PetscCall(DMNetworkGetConnectedVertices(dm, e, &cone));
2379       for (v = 0; v < 2; v++) {
2380         PetscCall(PetscSectionGetDof(network->DofSection, cone[v], &ncols));
2381 
2382         if (network->Je) {
2383           Juser = network->Je[3 * e + 1 + v]; /* Jacobian(e,v) */
2384         } else Juser = NULL;
2385         PetscCall(DMNetworkIsGhostVertex(dm, cone[v], &ghost));
2386         PetscCall(MatSetPreallocationblock_private(Juser, nrows, rows, ncols, ghost, vd_nz, vo_nz));
2387       }
2388 
2389       /* Set preallocation for edge self */
2390       cstart = rstart;
2391       if (network->Je) {
2392         Juser = network->Je[3 * e]; /* Jacobian(e,e) */
2393       } else Juser = NULL;
2394       PetscCall(MatSetPreallocationblock_private(Juser, nrows, rows, nrows, PETSC_FALSE, vd_nz, vo_nz));
2395     }
2396   }
2397 
2398   /* Set preallocation for vertices */
2399   /*--------------------------------*/
2400   PetscCall(DMNetworkGetVertexRange(dm, &vStart, &vEnd));
2401   if (vEnd - vStart) vptr = network->Jvptr;
2402 
2403   for (v = vStart; v < vEnd; v++) {
2404     /* Get row indices */
2405     PetscCall(DMNetworkGetGlobalVecOffset(dm, v, ALL_COMPONENTS, &rstart));
2406     PetscCall(PetscSectionGetDof(network->DofSection, v, &nrows));
2407     if (!nrows) continue;
2408 
2409     PetscCall(DMNetworkIsGhostVertex(dm, v, &ghost));
2410     if (ghost) {
2411       PetscCall(PetscMalloc1(nrows, &rows_v));
2412     } else {
2413       rows_v = rows;
2414     }
2415 
2416     for (j = 0; j < nrows; j++) rows_v[j] = j + rstart;
2417 
2418     /* Get supporting edges and connected vertices */
2419     PetscCall(DMNetworkGetSupportingEdges(dm, v, &nedges, &edges));
2420 
2421     for (e = 0; e < nedges; e++) {
2422       /* Supporting edges */
2423       PetscCall(DMNetworkGetGlobalVecOffset(dm, edges[e], ALL_COMPONENTS, &cstart));
2424       PetscCall(PetscSectionGetDof(network->DofSection, edges[e], &ncols));
2425 
2426       if (network->Jv) {
2427         Juser = network->Jv[vptr[v - vStart] + 2 * e + 1]; /* Jacobian(v,e) */
2428       } else Juser = NULL;
2429       PetscCall(MatSetPreallocationblock_private(Juser, nrows, rows_v, ncols, ghost, vd_nz, vo_nz));
2430 
2431       /* Connected vertices */
2432       PetscCall(DMNetworkGetConnectedVertices(dm, edges[e], &cone));
2433       vc = (v == cone[0]) ? cone[1] : cone[0];
2434       PetscCall(DMNetworkIsGhostVertex(dm, vc, &ghost_vc));
2435 
2436       PetscCall(PetscSectionGetDof(network->DofSection, vc, &ncols));
2437 
2438       if (network->Jv) {
2439         Juser = network->Jv[vptr[v - vStart] + 2 * e + 2]; /* Jacobian(v,vc) */
2440       } else Juser = NULL;
2441       if (ghost_vc || ghost) {
2442         ghost2 = PETSC_TRUE;
2443       } else {
2444         ghost2 = PETSC_FALSE;
2445       }
2446       PetscCall(MatSetPreallocationblock_private(Juser, nrows, rows_v, ncols, ghost2, vd_nz, vo_nz));
2447     }
2448 
2449     /* Set preallocation for vertex self */
2450     PetscCall(DMNetworkIsGhostVertex(dm, v, &ghost));
2451     if (!ghost) {
2452       PetscCall(DMNetworkGetGlobalVecOffset(dm, v, ALL_COMPONENTS, &cstart));
2453       if (network->Jv) {
2454         Juser = network->Jv[vptr[v - vStart]]; /* Jacobian(v,v) */
2455       } else Juser = NULL;
2456       PetscCall(MatSetPreallocationblock_private(Juser, nrows, rows_v, nrows, PETSC_FALSE, vd_nz, vo_nz));
2457     }
2458     if (ghost) PetscCall(PetscFree(rows_v));
2459   }
2460 
2461   PetscCall(VecAssemblyBegin(vd_nz));
2462   PetscCall(VecAssemblyBegin(vo_nz));
2463 
2464   PetscCall(PetscMalloc2(localSize, &dnnz, localSize, &onnz));
2465 
2466   PetscCall(VecAssemblyEnd(vd_nz));
2467   PetscCall(VecAssemblyEnd(vo_nz));
2468 
2469   PetscCall(VecGetArray(vd_nz, &vdnz));
2470   PetscCall(VecGetArray(vo_nz, &vonz));
2471   for (j = 0; j < localSize; j++) {
2472     dnnz[j] = (PetscInt)PetscRealPart(vdnz[j]);
2473     onnz[j] = (PetscInt)PetscRealPart(vonz[j]);
2474   }
2475   PetscCall(VecRestoreArray(vd_nz, &vdnz));
2476   PetscCall(VecRestoreArray(vo_nz, &vonz));
2477   PetscCall(VecDestroy(&vd_nz));
2478   PetscCall(VecDestroy(&vo_nz));
2479 
2480   PetscCall(MatSeqAIJSetPreallocation(*J, 0, dnnz));
2481   PetscCall(MatMPIAIJSetPreallocation(*J, 0, dnnz, 0, onnz));
2482   PetscCall(MatSetOption(*J, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE));
2483 
2484   PetscCall(PetscFree2(dnnz, onnz));
2485 
2486   /* (2) Set matrix entries for edges */
2487   /*----------------------------------*/
2488   for (e = eStart; e < eEnd; e++) {
2489     /* Get row indices */
2490     PetscCall(DMNetworkGetGlobalVecOffset(dm, e, ALL_COMPONENTS, &rstart));
2491     PetscCall(PetscSectionGetDof(network->DofSection, e, &nrows));
2492     if (nrows) {
2493       for (j = 0; j < nrows; j++) rows[j] = j + rstart;
2494 
2495       /* Set matrix entries for connected vertices */
2496       PetscCall(DMNetworkGetConnectedVertices(dm, e, &cone));
2497       for (v = 0; v < 2; v++) {
2498         PetscCall(DMNetworkGetGlobalVecOffset(dm, cone[v], ALL_COMPONENTS, &cstart));
2499         PetscCall(PetscSectionGetDof(network->DofSection, cone[v], &ncols));
2500 
2501         if (network->Je) {
2502           Juser = network->Je[3 * e + 1 + v]; /* Jacobian(e,v) */
2503         } else Juser = NULL;
2504         PetscCall(MatSetblock_private(Juser, nrows, rows, ncols, cstart, J));
2505       }
2506 
2507       /* Set matrix entries for edge self */
2508       cstart = rstart;
2509       if (network->Je) {
2510         Juser = network->Je[3 * e]; /* Jacobian(e,e) */
2511       } else Juser = NULL;
2512       PetscCall(MatSetblock_private(Juser, nrows, rows, nrows, cstart, J));
2513     }
2514   }
2515 
2516   /* Set matrix entries for vertices */
2517   /*---------------------------------*/
2518   for (v = vStart; v < vEnd; v++) {
2519     /* Get row indices */
2520     PetscCall(DMNetworkGetGlobalVecOffset(dm, v, ALL_COMPONENTS, &rstart));
2521     PetscCall(PetscSectionGetDof(network->DofSection, v, &nrows));
2522     if (!nrows) continue;
2523 
2524     PetscCall(DMNetworkIsGhostVertex(dm, v, &ghost));
2525     if (ghost) {
2526       PetscCall(PetscMalloc1(nrows, &rows_v));
2527     } else {
2528       rows_v = rows;
2529     }
2530     for (j = 0; j < nrows; j++) rows_v[j] = j + rstart;
2531 
2532     /* Get supporting edges and connected vertices */
2533     PetscCall(DMNetworkGetSupportingEdges(dm, v, &nedges, &edges));
2534 
2535     for (e = 0; e < nedges; e++) {
2536       /* Supporting edges */
2537       PetscCall(DMNetworkGetGlobalVecOffset(dm, edges[e], ALL_COMPONENTS, &cstart));
2538       PetscCall(PetscSectionGetDof(network->DofSection, edges[e], &ncols));
2539 
2540       if (network->Jv) {
2541         Juser = network->Jv[vptr[v - vStart] + 2 * e + 1]; /* Jacobian(v,e) */
2542       } else Juser = NULL;
2543       PetscCall(MatSetblock_private(Juser, nrows, rows_v, ncols, cstart, J));
2544 
2545       /* Connected vertices */
2546       PetscCall(DMNetworkGetConnectedVertices(dm, edges[e], &cone));
2547       vc = (v == cone[0]) ? cone[1] : cone[0];
2548 
2549       PetscCall(DMNetworkGetGlobalVecOffset(dm, vc, ALL_COMPONENTS, &cstart));
2550       PetscCall(PetscSectionGetDof(network->DofSection, vc, &ncols));
2551 
2552       if (network->Jv) {
2553         Juser = network->Jv[vptr[v - vStart] + 2 * e + 2]; /* Jacobian(v,vc) */
2554       } else Juser = NULL;
2555       PetscCall(MatSetblock_private(Juser, nrows, rows_v, ncols, cstart, J));
2556     }
2557 
2558     /* Set matrix entries for vertex self */
2559     if (!ghost) {
2560       PetscCall(DMNetworkGetGlobalVecOffset(dm, v, ALL_COMPONENTS, &cstart));
2561       if (network->Jv) {
2562         Juser = network->Jv[vptr[v - vStart]]; /* Jacobian(v,v) */
2563       } else Juser = NULL;
2564       PetscCall(MatSetblock_private(Juser, nrows, rows_v, nrows, cstart, J));
2565     }
2566     if (ghost) PetscCall(PetscFree(rows_v));
2567   }
2568   PetscCall(PetscFree(rows));
2569 
2570   PetscCall(MatAssemblyBegin(*J, MAT_FINAL_ASSEMBLY));
2571   PetscCall(MatAssemblyEnd(*J, MAT_FINAL_ASSEMBLY));
2572 
2573   PetscCall(MatSetDM(*J, dm));
2574   PetscFunctionReturn(0);
2575 }
2576 
2577 static PetscErrorCode DMNetworkDestroyComponentData(DM dm)
2578 {
2579   DM_Network *network = (DM_Network *)dm->data;
2580   PetscInt    j, np;
2581 
2582   PetscFunctionBegin;
2583   if (network->header) {
2584     np = network->cloneshared->pEnd - network->cloneshared->pStart;
2585     for (j = 0; j < np; j++) {
2586       PetscCall(PetscFree5(network->header[j].size, network->header[j].key, network->header[j].offset, network->header[j].nvar, network->header[j].offsetvarrel));
2587       PetscCall(PetscFree(network->cvalue[j].data));
2588     }
2589     PetscCall(PetscFree2(network->header, network->cvalue));
2590   }
2591   PetscFunctionReturn(0);
2592 }
2593 
2594 PetscErrorCode DMDestroy_Network(DM dm)
2595 {
2596   DM_Network *network = (DM_Network *)dm->data;
2597   PetscInt    j;
2598 
2599   PetscFunctionBegin;
2600   /*
2601     Developers Note: Due to the mixed topological definition of DMNetwork and data defined ON the
2602     network like DofSection, DataSection, *componentdataarray, and friends, when cloning, we share
2603     only the true topological data, and make our own data ON the network. Thus refct only refers
2604     to the number of references to topological data, and data ON the network is always destroyed.
2605     It is understood this is atypical for a DM, but is very intentional.
2606   */
2607 
2608   /* Always destroy data ON the network */
2609   PetscCall(PetscFree(network->Je));
2610   if (network->Jv) {
2611     PetscCall(PetscFree(network->Jvptr));
2612     PetscCall(PetscFree(network->Jv));
2613   }
2614   PetscCall(PetscSectionDestroy(&network->DataSection));
2615   PetscCall(PetscSectionDestroy(&network->DofSection));
2616   PetscCall(PetscFree(network->component));
2617   PetscCall(PetscFree(network->componentdataarray));
2618   PetscCall(DMNetworkDestroyComponentData(dm));
2619 
2620   PetscCall(DMDestroy(&network->plex)); /* this is cloned in DMClone_Network, so safe to destroy */
2621 
2622   /*
2623   Developers Note: The DMNetworkVertexInfo and DMNetworkEdgeInfo data structures are completely
2624   destroyed as they are again a mix of topological data:
2625     ISLocalToGlobalMapping            mapping;
2626     PetscSF                           sf;
2627   and data ON the network
2628     PetscSection                      DofSection;
2629     PetscSection                      GlobalDofSection;
2630   And the only way to access them currently is through DMNetworkAssembleGraphStructures which assembles
2631   everything. So we must destroy everything and require DMNetworkAssembleGraphStructures is called again
2632   for any clone.
2633   */
2634   PetscCall(ISLocalToGlobalMappingDestroy(&network->vertex.mapping));
2635   PetscCall(PetscSectionDestroy(&network->vertex.DofSection));
2636   PetscCall(PetscSectionDestroy(&network->vertex.GlobalDofSection));
2637   PetscCall(PetscSFDestroy(&network->vertex.sf));
2638   /* edge */
2639   PetscCall(ISLocalToGlobalMappingDestroy(&network->edge.mapping));
2640   PetscCall(PetscSectionDestroy(&network->edge.DofSection));
2641   PetscCall(PetscSectionDestroy(&network->edge.GlobalDofSection));
2642   PetscCall(PetscSFDestroy(&network->edge.sf));
2643   /* Destroy the potentially cloneshared data */
2644   if (--network->cloneshared->refct <= 0) {
2645     /* Developer Note: I'm not sure if vltog can be reused or not, as I'm not sure what it's purpose is. I
2646      naively think it can be reused. */
2647     PetscCall(PetscFree(network->cloneshared->vltog));
2648     for (j = 0; j < network->cloneshared->Nsvtx; j++) PetscCall(PetscFree(network->cloneshared->svtx[j].sv));
2649     PetscCall(PetscFree(network->cloneshared->svtx));
2650     PetscCall(PetscFree2(network->cloneshared->subnetedge, network->cloneshared->subnetvtx));
2651     PetscCall(PetscTableDestroy(&network->cloneshared->svtable));
2652     PetscCall(PetscFree(network->cloneshared->subnet));
2653     PetscCall(PetscFree(network->cloneshared));
2654   }
2655   PetscCall(PetscFree(network)); /* Always freed as this structure is copied in a clone, not cloneshared */
2656   PetscFunctionReturn(0);
2657 }
2658 
2659 PetscErrorCode DMView_Network(DM dm, PetscViewer viewer)
2660 {
2661   PetscBool   iascii;
2662   PetscMPIInt rank;
2663 
2664   PetscFunctionBegin;
2665   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2666   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2667   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
2668   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
2669   if (iascii) {
2670     const PetscInt *cone, *vtx, *edges;
2671     PetscInt        vfrom, vto, i, j, nv, ne, nsv, p, nsubnet;
2672     DM_Network     *network = (DM_Network *)dm->data;
2673 
2674     nsubnet = network->cloneshared->Nsubnet; /* num of subnetworks */
2675     if (rank == 0) {
2676       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,
2677                             network->cloneshared->Nsvtx));
2678     }
2679 
2680     PetscCall(DMNetworkGetSharedVertices(dm, &nsv, NULL));
2681     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
2682     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  [%d] nEdges: %" PetscInt_FMT "; nVertices: %" PetscInt_FMT "; nSharedVertices: %" PetscInt_FMT "\n", rank, network->cloneshared->nEdges, network->cloneshared->nVertices, nsv));
2683 
2684     for (i = 0; i < nsubnet; i++) {
2685       PetscCall(DMNetworkGetSubnetwork(dm, i, &nv, &ne, &vtx, &edges));
2686       if (ne) {
2687         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "     Subnet %" PetscInt_FMT ": nEdges %" PetscInt_FMT ", nVertices(include shared vertices) %" PetscInt_FMT "\n", i, ne, nv));
2688         for (j = 0; j < ne; j++) {
2689           p = edges[j];
2690           PetscCall(DMNetworkGetConnectedVertices(dm, p, &cone));
2691           PetscCall(DMNetworkGetGlobalVertexIndex(dm, cone[0], &vfrom));
2692           PetscCall(DMNetworkGetGlobalVertexIndex(dm, cone[1], &vto));
2693           PetscCall(DMNetworkGetGlobalEdgeIndex(dm, edges[j], &p));
2694           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "       edge %" PetscInt_FMT ": %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", p, vfrom, vto));
2695         }
2696       }
2697     }
2698 
2699     /* Shared vertices */
2700     PetscCall(DMNetworkGetSharedVertices(dm, NULL, &vtx));
2701     if (nsv) {
2702       PetscInt        gidx;
2703       PetscBool       ghost;
2704       const PetscInt *sv = NULL;
2705 
2706       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "     SharedVertices:\n"));
2707       for (i = 0; i < nsv; i++) {
2708         PetscCall(DMNetworkIsGhostVertex(dm, vtx[i], &ghost));
2709         if (ghost) continue;
2710 
2711         PetscCall(DMNetworkSharedVertexGetInfo(dm, vtx[i], &gidx, &nv, &sv));
2712         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "       svtx %" PetscInt_FMT ": global index %" PetscInt_FMT ", subnet[%" PetscInt_FMT "].%" PetscInt_FMT " ---->\n", i, gidx, sv[0], sv[1]));
2713         for (j = 1; j < nv; j++) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "                                           ----> subnet[%" PetscInt_FMT "].%" PetscInt_FMT "\n", sv[2 * j], sv[2 * j + 1]));
2714       }
2715     }
2716     PetscCall(PetscViewerFlush(viewer));
2717     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
2718   } else PetscCheck(iascii, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMNetwork writing", ((PetscObject)viewer)->type_name);
2719   PetscFunctionReturn(0);
2720 }
2721 
2722 PetscErrorCode DMGlobalToLocalBegin_Network(DM dm, Vec g, InsertMode mode, Vec l)
2723 {
2724   DM_Network *network = (DM_Network *)dm->data;
2725 
2726   PetscFunctionBegin;
2727   PetscCall(DMGlobalToLocalBegin(network->plex, g, mode, l));
2728   PetscFunctionReturn(0);
2729 }
2730 
2731 PetscErrorCode DMGlobalToLocalEnd_Network(DM dm, Vec g, InsertMode mode, Vec l)
2732 {
2733   DM_Network *network = (DM_Network *)dm->data;
2734 
2735   PetscFunctionBegin;
2736   PetscCall(DMGlobalToLocalEnd(network->plex, g, mode, l));
2737   PetscFunctionReturn(0);
2738 }
2739 
2740 PetscErrorCode DMLocalToGlobalBegin_Network(DM dm, Vec l, InsertMode mode, Vec g)
2741 {
2742   DM_Network *network = (DM_Network *)dm->data;
2743 
2744   PetscFunctionBegin;
2745   PetscCall(DMLocalToGlobalBegin(network->plex, l, mode, g));
2746   PetscFunctionReturn(0);
2747 }
2748 
2749 PetscErrorCode DMLocalToGlobalEnd_Network(DM dm, Vec l, InsertMode mode, Vec g)
2750 {
2751   DM_Network *network = (DM_Network *)dm->data;
2752 
2753   PetscFunctionBegin;
2754   PetscCall(DMLocalToGlobalEnd(network->plex, l, mode, g));
2755   PetscFunctionReturn(0);
2756 }
2757 
2758 /*@
2759   DMNetworkGetVertexLocalToGlobalOrdering - Get vertex global index
2760 
2761   Not collective
2762 
2763   Input Parameters:
2764 + dm - the dm object
2765 - vloc - local vertex ordering, start from 0
2766 
2767   Output Parameters:
2768 .  vg  - global vertex ordering, start from 0
2769 
2770   Level: advanced
2771 
2772 .seealso: `DMNetworkSetVertexLocalToGlobalOrdering()`
2773 @*/
2774 PetscErrorCode DMNetworkGetVertexLocalToGlobalOrdering(DM dm, PetscInt vloc, PetscInt *vg)
2775 {
2776   DM_Network *network = (DM_Network *)dm->data;
2777   PetscInt   *vltog   = network->cloneshared->vltog;
2778 
2779   PetscFunctionBegin;
2780   PetscCheck(vltog, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Must call DMNetworkSetVertexLocalToGlobalOrdering() first");
2781   *vg = vltog[vloc];
2782   PetscFunctionReturn(0);
2783 }
2784 
2785 /*@
2786   DMNetworkSetVertexLocalToGlobalOrdering - Create and setup vertex local to global map
2787 
2788   Collective
2789 
2790   Input Parameters:
2791 . dm - the dm object
2792 
2793   Level: advanced
2794 
2795 .seealso: `DMNetworkGetGlobalVertexIndex()`
2796 @*/
2797 PetscErrorCode DMNetworkSetVertexLocalToGlobalOrdering(DM dm)
2798 {
2799   DM_Network        *network = (DM_Network *)dm->data;
2800   MPI_Comm           comm;
2801   PetscMPIInt        rank, size, *displs = NULL, *recvcounts = NULL, remoterank;
2802   PetscBool          ghost;
2803   PetscInt          *vltog, nroots, nleaves, i, *vrange, k, N, lidx;
2804   const PetscSFNode *iremote;
2805   PetscSF            vsf;
2806   Vec                Vleaves, Vleaves_seq;
2807   VecScatter         ctx;
2808   PetscScalar       *varr, val;
2809   const PetscScalar *varr_read;
2810 
2811   PetscFunctionBegin;
2812   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
2813   PetscCallMPI(MPI_Comm_size(comm, &size));
2814   PetscCallMPI(MPI_Comm_rank(comm, &rank));
2815 
2816   if (size == 1) {
2817     nroots = network->cloneshared->vEnd - network->cloneshared->vStart;
2818     PetscCall(PetscMalloc1(nroots, &vltog));
2819     for (i = 0; i < nroots; i++) vltog[i] = i;
2820     network->cloneshared->vltog = vltog;
2821     PetscFunctionReturn(0);
2822   }
2823 
2824   PetscCheck(network->cloneshared->distributecalled, comm, PETSC_ERR_ARG_WRONGSTATE, "Must call DMNetworkDistribute() first");
2825   if (network->cloneshared->vltog) PetscCall(PetscFree(network->cloneshared->vltog));
2826 
2827   PetscCall(DMNetworkSetSubMap_private(network->cloneshared->vStart, network->cloneshared->vEnd, &network->vertex.mapping));
2828   PetscCall(PetscSFGetSubSF(network->plex->sf, network->vertex.mapping, &network->vertex.sf));
2829   vsf = network->vertex.sf;
2830 
2831   PetscCall(PetscMalloc3(size + 1, &vrange, size, &displs, size, &recvcounts));
2832   PetscCall(PetscSFGetGraph(vsf, &nroots, &nleaves, NULL, &iremote));
2833 
2834   for (i = 0; i < size; i++) {
2835     displs[i]     = i;
2836     recvcounts[i] = 1;
2837   }
2838 
2839   i         = nroots - nleaves; /* local number of vertices, excluding ghosts */
2840   vrange[0] = 0;
2841   PetscCallMPI(MPI_Allgatherv(&i, 1, MPIU_INT, vrange + 1, recvcounts, displs, MPIU_INT, comm));
2842   for (i = 2; i < size + 1; i++) vrange[i] += vrange[i - 1];
2843 
2844   PetscCall(PetscMalloc1(nroots, &vltog));
2845   network->cloneshared->vltog = vltog;
2846 
2847   /* Set vltog for non-ghost vertices */
2848   k = 0;
2849   for (i = 0; i < nroots; i++) {
2850     PetscCall(DMNetworkIsGhostVertex(dm, i + network->cloneshared->vStart, &ghost));
2851     if (ghost) continue;
2852     vltog[i] = vrange[rank] + k++;
2853   }
2854   PetscCall(PetscFree3(vrange, displs, recvcounts));
2855 
2856   /* Set vltog for ghost vertices */
2857   /* (a) create parallel Vleaves and sequential Vleaves_seq to convert local iremote[*].index to global index */
2858   PetscCall(VecCreate(comm, &Vleaves));
2859   PetscCall(VecSetSizes(Vleaves, 2 * nleaves, PETSC_DETERMINE));
2860   PetscCall(VecSetFromOptions(Vleaves));
2861   PetscCall(VecGetArray(Vleaves, &varr));
2862   for (i = 0; i < nleaves; i++) {
2863     varr[2 * i]     = (PetscScalar)(iremote[i].rank);  /* rank of remote process */
2864     varr[2 * i + 1] = (PetscScalar)(iremote[i].index); /* local index in remote process */
2865   }
2866   PetscCall(VecRestoreArray(Vleaves, &varr));
2867 
2868   /* (b) scatter local info to remote processes via VecScatter() */
2869   PetscCall(VecScatterCreateToAll(Vleaves, &ctx, &Vleaves_seq));
2870   PetscCall(VecScatterBegin(ctx, Vleaves, Vleaves_seq, INSERT_VALUES, SCATTER_FORWARD));
2871   PetscCall(VecScatterEnd(ctx, Vleaves, Vleaves_seq, INSERT_VALUES, SCATTER_FORWARD));
2872 
2873   /* (c) convert local indices to global indices in parallel vector Vleaves */
2874   PetscCall(VecGetSize(Vleaves_seq, &N));
2875   PetscCall(VecGetArrayRead(Vleaves_seq, &varr_read));
2876   for (i = 0; i < N; i += 2) {
2877     remoterank = (PetscMPIInt)PetscRealPart(varr_read[i]);
2878     if (remoterank == rank) {
2879       k    = i + 1; /* row number */
2880       lidx = (PetscInt)PetscRealPart(varr_read[i + 1]);
2881       val  = (PetscScalar)vltog[lidx]; /* global index for non-ghost vertex computed above */
2882       PetscCall(VecSetValues(Vleaves, 1, &k, &val, INSERT_VALUES));
2883     }
2884   }
2885   PetscCall(VecRestoreArrayRead(Vleaves_seq, &varr_read));
2886   PetscCall(VecAssemblyBegin(Vleaves));
2887   PetscCall(VecAssemblyEnd(Vleaves));
2888 
2889   /* (d) Set vltog for ghost vertices by copying local values of Vleaves */
2890   PetscCall(VecGetArrayRead(Vleaves, &varr_read));
2891   k = 0;
2892   for (i = 0; i < nroots; i++) {
2893     PetscCall(DMNetworkIsGhostVertex(dm, i + network->cloneshared->vStart, &ghost));
2894     if (!ghost) continue;
2895     vltog[i] = (PetscInt)PetscRealPart(varr_read[2 * k + 1]);
2896     k++;
2897   }
2898   PetscCall(VecRestoreArrayRead(Vleaves, &varr_read));
2899 
2900   PetscCall(VecDestroy(&Vleaves));
2901   PetscCall(VecDestroy(&Vleaves_seq));
2902   PetscCall(VecScatterDestroy(&ctx));
2903   PetscFunctionReturn(0);
2904 }
2905 
2906 static inline PetscErrorCode DMISAddSize_private(DM_Network *network, PetscInt p, PetscInt numkeys, PetscInt keys[], PetscInt blocksize[], PetscInt nselectedvar[], PetscInt *nidx)
2907 {
2908   PetscInt                 i, j, ncomps, nvar, key, offset = 0;
2909   DMNetworkComponentHeader header;
2910 
2911   PetscFunctionBegin;
2912   PetscCall(PetscSectionGetOffset(network->DataSection, p, &offset));
2913   ncomps = ((DMNetworkComponentHeader)(network->componentdataarray + offset))->ndata;
2914   header = (DMNetworkComponentHeader)(network->componentdataarray + offset);
2915 
2916   for (i = 0; i < ncomps; i++) {
2917     key  = header->key[i];
2918     nvar = header->nvar[i];
2919     for (j = 0; j < numkeys; j++) {
2920       if (key == keys[j]) {
2921         if (!blocksize || blocksize[j] == -1) {
2922           *nidx += nvar;
2923         } else {
2924           *nidx += nselectedvar[j] * nvar / blocksize[j];
2925         }
2926       }
2927     }
2928   }
2929   PetscFunctionReturn(0);
2930 }
2931 
2932 static inline PetscErrorCode DMISComputeIdx_private(DM dm, PetscInt p, PetscInt numkeys, PetscInt keys[], PetscInt blocksize[], PetscInt nselectedvar[], PetscInt *selectedvar[], PetscInt *ii, PetscInt *idx)
2933 {
2934   PetscInt                 i, j, ncomps, nvar, key, offsetg, k, k1, offset = 0;
2935   DM_Network              *network = (DM_Network *)dm->data;
2936   DMNetworkComponentHeader header;
2937 
2938   PetscFunctionBegin;
2939   PetscCall(PetscSectionGetOffset(network->DataSection, p, &offset));
2940   ncomps = ((DMNetworkComponentHeader)(network->componentdataarray + offset))->ndata;
2941   header = (DMNetworkComponentHeader)(network->componentdataarray + offset);
2942 
2943   for (i = 0; i < ncomps; i++) {
2944     key  = header->key[i];
2945     nvar = header->nvar[i];
2946     for (j = 0; j < numkeys; j++) {
2947       if (key != keys[j]) continue;
2948 
2949       PetscCall(DMNetworkGetGlobalVecOffset(dm, p, i, &offsetg));
2950       if (!blocksize || blocksize[j] == -1) {
2951         for (k = 0; k < nvar; k++) idx[(*ii)++] = offsetg + k;
2952       } else {
2953         for (k = 0; k < nvar; k += blocksize[j]) {
2954           for (k1 = 0; k1 < nselectedvar[j]; k1++) idx[(*ii)++] = offsetg + k + selectedvar[j][k1];
2955         }
2956       }
2957     }
2958   }
2959   PetscFunctionReturn(0);
2960 }
2961 
2962 /*@
2963   DMNetworkCreateIS - Create an index set object from the global vector of the network
2964 
2965   Collective
2966 
2967   Input Parameters:
2968 + dm - DMNetwork object
2969 . numkeys - number of keys
2970 . keys - array of keys that define the components of the variables you wish to extract
2971 . blocksize - block size of the variables associated to the component
2972 . nselectedvar - number of variables in each block to select
2973 - selectedvar - the offset into the block of each variable in each block to select
2974 
2975   Output Parameters:
2976 . is - the index set
2977 
2978   Level: Advanced
2979 
2980   Notes:
2981     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.
2982 
2983 .seealso: `DMNetworkCreate()`, `ISCreateGeneral()`, `DMNetworkCreateLocalIS()`
2984 @*/
2985 PetscErrorCode DMNetworkCreateIS(DM dm, PetscInt numkeys, PetscInt keys[], PetscInt blocksize[], PetscInt nselectedvar[], PetscInt *selectedvar[], IS *is)
2986 {
2987   MPI_Comm    comm;
2988   DM_Network *network = (DM_Network *)dm->data;
2989   PetscInt    i, p, estart, eend, vstart, vend, nidx, *idx;
2990   PetscBool   ghost;
2991 
2992   PetscFunctionBegin;
2993   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
2994 
2995   /* Check input parameters */
2996   for (i = 0; i < numkeys; i++) {
2997     if (!blocksize || blocksize[i] == -1) continue;
2998     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]);
2999   }
3000 
3001   PetscCall(DMNetworkGetEdgeRange(dm, &estart, &eend));
3002   PetscCall(DMNetworkGetVertexRange(dm, &vstart, &vend));
3003 
3004   /* Get local number of idx */
3005   nidx = 0;
3006   for (p = estart; p < eend; p++) PetscCall(DMISAddSize_private(network, p, numkeys, keys, blocksize, nselectedvar, &nidx));
3007   for (p = vstart; p < vend; p++) {
3008     PetscCall(DMNetworkIsGhostVertex(dm, p, &ghost));
3009     if (ghost) continue;
3010     PetscCall(DMISAddSize_private(network, p, numkeys, keys, blocksize, nselectedvar, &nidx));
3011   }
3012 
3013   /* Compute idx */
3014   PetscCall(PetscMalloc1(nidx, &idx));
3015   i = 0;
3016   for (p = estart; p < eend; p++) PetscCall(DMISComputeIdx_private(dm, p, numkeys, keys, blocksize, nselectedvar, selectedvar, &i, idx));
3017   for (p = vstart; p < vend; p++) {
3018     PetscCall(DMNetworkIsGhostVertex(dm, p, &ghost));
3019     if (ghost) continue;
3020     PetscCall(DMISComputeIdx_private(dm, p, numkeys, keys, blocksize, nselectedvar, selectedvar, &i, idx));
3021   }
3022 
3023   /* Create is */
3024   PetscCall(ISCreateGeneral(comm, nidx, idx, PETSC_COPY_VALUES, is));
3025   PetscCall(PetscFree(idx));
3026   PetscFunctionReturn(0);
3027 }
3028 
3029 static inline PetscErrorCode DMISComputeLocalIdx_private(DM dm, PetscInt p, PetscInt numkeys, PetscInt keys[], PetscInt blocksize[], PetscInt nselectedvar[], PetscInt *selectedvar[], PetscInt *ii, PetscInt *idx)
3030 {
3031   PetscInt                 i, j, ncomps, nvar, key, offsetl, k, k1, offset = 0;
3032   DM_Network              *network = (DM_Network *)dm->data;
3033   DMNetworkComponentHeader header;
3034 
3035   PetscFunctionBegin;
3036   PetscCall(PetscSectionGetOffset(network->DataSection, p, &offset));
3037   ncomps = ((DMNetworkComponentHeader)(network->componentdataarray + offset))->ndata;
3038   header = (DMNetworkComponentHeader)(network->componentdataarray + offset);
3039 
3040   for (i = 0; i < ncomps; i++) {
3041     key  = header->key[i];
3042     nvar = header->nvar[i];
3043     for (j = 0; j < numkeys; j++) {
3044       if (key != keys[j]) continue;
3045 
3046       PetscCall(DMNetworkGetLocalVecOffset(dm, p, i, &offsetl));
3047       if (!blocksize || blocksize[j] == -1) {
3048         for (k = 0; k < nvar; k++) idx[(*ii)++] = offsetl + k;
3049       } else {
3050         for (k = 0; k < nvar; k += blocksize[j]) {
3051           for (k1 = 0; k1 < nselectedvar[j]; k1++) idx[(*ii)++] = offsetl + k + selectedvar[j][k1];
3052         }
3053       }
3054     }
3055   }
3056   PetscFunctionReturn(0);
3057 }
3058 
3059 /*@
3060   DMNetworkCreateLocalIS - Create an index set object from the local vector of the network
3061 
3062   Not collective
3063 
3064   Input Parameters:
3065 + dm - DMNetwork object
3066 . numkeys - number of keys
3067 . keys - array of keys that define the components of the variables you wish to extract
3068 . blocksize - block size of the variables associated to the component
3069 . nselectedvar - number of variables in each block to select
3070 - selectedvar - the offset into the block of each variable in each block to select
3071 
3072   Output Parameters:
3073 . is - the index set
3074 
3075   Level: Advanced
3076 
3077   Notes:
3078     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.
3079 
3080 .seealso: `DMNetworkCreate()`, `DMNetworkCreateIS`, `ISCreateGeneral()`
3081 @*/
3082 PetscErrorCode DMNetworkCreateLocalIS(DM dm, PetscInt numkeys, PetscInt keys[], PetscInt blocksize[], PetscInt nselectedvar[], PetscInt *selectedvar[], IS *is)
3083 {
3084   DM_Network *network = (DM_Network *)dm->data;
3085   PetscInt    i, p, pstart, pend, nidx, *idx;
3086 
3087   PetscFunctionBegin;
3088   /* Check input parameters */
3089   for (i = 0; i < numkeys; i++) {
3090     if (!blocksize || blocksize[i] == -1) continue;
3091     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]);
3092   }
3093 
3094   pstart = network->cloneshared->pStart;
3095   pend   = network->cloneshared->pEnd;
3096 
3097   /* Get local number of idx */
3098   nidx = 0;
3099   for (p = pstart; p < pend; p++) PetscCall(DMISAddSize_private(network, p, numkeys, keys, blocksize, nselectedvar, &nidx));
3100 
3101   /* Compute local idx */
3102   PetscCall(PetscMalloc1(nidx, &idx));
3103   i = 0;
3104   for (p = pstart; p < pend; p++) PetscCall(DMISComputeLocalIdx_private(dm, p, numkeys, keys, blocksize, nselectedvar, selectedvar, &i, idx));
3105 
3106   /* Create is */
3107   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, nidx, idx, PETSC_COPY_VALUES, is));
3108   PetscCall(PetscFree(idx));
3109   PetscFunctionReturn(0);
3110 }
3111 /*@
3112   DMNetworkFinalizeComponents - Sets up internal data structures for the sections and components. It is called after registering new components and adding all components
3113   to the cloned network. After calling this subroutine, no new components can be added to the network.
3114 
3115   Collective
3116 
3117   Input Parameters:
3118 . dm - the dmnetwork object
3119 
3120   Level: beginner
3121 
3122 .seealso: `DMNetworkAddComponent()`, `DMNetworkRegisterComponent()`, `DMSetUp()`
3123 @*/
3124 PetscErrorCode DMNetworkFinalizeComponents(DM dm)
3125 {
3126   DM_Network *network = (DM_Network *)dm->data;
3127 
3128   PetscFunctionBegin;
3129   if (network->componentsetup) PetscFunctionReturn(0);
3130   PetscCall(DMNetworkComponentSetUp(dm));
3131   PetscCall(DMNetworkVariablesSetUp(dm));
3132   PetscCall(DMSetLocalSection(network->plex, network->DofSection));
3133   PetscCall(DMGetGlobalSection(network->plex, &network->GlobalDofSection));
3134   network->componentsetup = PETSC_TRUE;
3135   PetscFunctionReturn(0);
3136 }
3137