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