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