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