xref: /petsc/src/snes/tutorials/network/ex1.c (revision 9fd865de364bf685f9f753bc72082bc51a6e6586)
1 static char help[] = "This example demonstrates the use of DMNetwork with subnetworks for solving a coupled nonlinear \n\
2                       electric power grid and water pipe problem.\n\
3                       The available solver options are in the ex1options file \n\
4                       and the data files are in the datafiles of subdirectories.\n\
5                       Run this program: mpiexec -n <n> ./ex1 \n\\n";
6 /*
7   Example:
8     mpiexec -n 3 ./ex1 -petscpartitioner_type parmetis -dmnetwork_view draw -dmnetwork_view_distributed draw -dmnetwork_view_rank_range 0,1,2
9     mpiexec -n 3 ./ex1 -petscpartitioner_type simple -dmnetwork_view_distributed draw -dmnetwork_view_zoomin_vertices 0 -dmnetwork_view_zoomin_vertices_padding 2 -dmnetwork_view_rank_range 0
10     mpiexec -n <n> ./ex1 -monitorIteration -monitorColor -power_snes_max_it 0 -water_snes_max_it 0 -coupled_snes_max_it 10 -draw_pause 5.0
11 */
12 
13 #include "power/power.h"
14 #include "water/water.h"
15 
16 typedef struct {
17   UserCtx_Power appctx_power;
18   AppCtx_Water  appctx_water;
19   PetscInt      subsnes_id; /* snes solver id */
20   PetscInt      it;         /* iteration number */
21   Vec           localXold;  /* store previous solution, used by FormFunction_Dummy() */
22   PetscBool     monitorColor;
23 } UserCtx;
24 
25 /*
26   UserMonitor -- called at the end of every SNES iteration via option `-monitorIteration' or `-monitorColor'
27 */
28 PetscErrorCode UserMonitor(SNES snes, PetscInt its, PetscReal fnorm, void *appctx)
29 {
30   UserCtx    *user = (UserCtx *)appctx;
31   PetscMPIInt rank;
32   MPI_Comm    comm;
33   PetscInt    it;
34 
35   PetscFunctionBegin;
36   PetscCall(PetscObjectGetComm((PetscObject)snes, &comm));
37   PetscCallMPI(MPI_Comm_rank(comm, &rank));
38 
39   PetscCall(SNESGetIterationNumber(snes, &it));
40   if (rank == 0) {
41     PetscCall(SNESGetIterationNumber(snes, &it));
42     if (user->subsnes_id == 0 || user->subsnes_id == 1) {
43       PetscCall(PetscPrintf(PETSC_COMM_SELF, " subsnes_id %" PetscInt_FMT ", it %" PetscInt_FMT ", fnorm %g\n", user->subsnes_id, it, (double)fnorm));
44     } else {
45       PetscCall(PetscPrintf(PETSC_COMM_SELF, "   coupled_snes_it %" PetscInt_FMT ", total_snes_it %" PetscInt_FMT ", fnorm %g\n", it, user->it, (double)fnorm));
46     }
47   }
48 
49   if (user->monitorColor) {
50     DM           networkdm, dmcoords;
51     Vec          F;
52     PetscInt     v, vStart, vEnd, offset, gidx, rstart;
53     PetscReal   *color;
54     PetscScalar *farr;
55     PetscBool    ghost;
56 
57     PetscCall(SNESGetDM(snes, &networkdm));
58     PetscCall(DMGetCoordinateDM(networkdm, &dmcoords));
59 
60     PetscCall(SNESGetFunction(snes, &F, NULL, NULL));
61     PetscCall(VecGetOwnershipRange(F, &rstart, NULL));
62 
63     PetscCall(VecGetArray(F, &farr));
64     PetscCall(DMNetworkGetVertexRange(dmcoords, &vStart, &vEnd));
65 
66     PetscCall(PetscPrintf(MPI_COMM_WORLD, "\nColorPrint:\n"));
67     for (v = vStart; v < vEnd; v++) {
68       PetscCall(DMNetworkIsGhostVertex(networkdm, v, &ghost));
69       PetscCall(DMNetworkGetComponent(dmcoords, v, 0, NULL, (void **)&color, NULL));
70       PetscCall(DMNetworkGetGlobalVertexIndex(networkdm, v, &gidx));
71       if (!ghost) {
72         PetscCall(DMNetworkGetGlobalVecOffset(networkdm, v, 0, &offset));
73         *color = (PetscRealPart(farr[offset - rstart]));
74       }
75       PetscCall(PetscSynchronizedPrintf(MPI_COMM_WORLD, "[%d] v %" PetscInt_FMT ": color[%" PetscInt_FMT "] = %g\n", rank, gidx, offset - rstart, *color));
76     }
77     PetscCall(PetscSynchronizedFlush(MPI_COMM_WORLD, NULL));
78     PetscCall(VecRestoreArray(F, &farr));
79 
80     PetscCall(PetscViewerPushFormat(PETSC_VIEWER_STDOUT_WORLD, PETSC_VIEWER_ASCII_CSV));
81     PetscCall(DMView(networkdm, PETSC_VIEWER_DRAW_WORLD));
82     PetscCall(PetscViewerPopFormat(PETSC_VIEWER_STDOUT_WORLD));
83   }
84   PetscFunctionReturn(PETSC_SUCCESS);
85 }
86 
87 PetscErrorCode FormJacobian_subPower(SNES snes, Vec X, Mat J, Mat Jpre, void *appctx)
88 {
89   DM              networkdm;
90   Vec             localX;
91   PetscInt        nv, ne, i, j, offset, nvar, row;
92   const PetscInt *vtx, *edges;
93   PetscBool       ghostvtex;
94   PetscScalar     one = 1.0;
95   PetscMPIInt     rank;
96   MPI_Comm        comm;
97 
98   PetscFunctionBegin;
99   PetscCall(SNESGetDM(snes, &networkdm));
100   PetscCall(DMGetLocalVector(networkdm, &localX));
101 
102   PetscCall(PetscObjectGetComm((PetscObject)networkdm, &comm));
103   PetscCallMPI(MPI_Comm_rank(comm, &rank));
104 
105   PetscCall(DMGlobalToLocalBegin(networkdm, X, INSERT_VALUES, localX));
106   PetscCall(DMGlobalToLocalEnd(networkdm, X, INSERT_VALUES, localX));
107 
108   PetscCall(MatZeroEntries(J));
109 
110   /* Power subnetwork: copied from power/FormJacobian_Power() */
111   PetscCall(DMNetworkGetSubnetwork(networkdm, 0, &nv, &ne, &vtx, &edges));
112   PetscCall(FormJacobian_Power_private(networkdm, localX, J, nv, ne, vtx, edges, appctx));
113 
114   /* Water subnetwork: Identity */
115   PetscCall(DMNetworkGetSubnetwork(networkdm, 1, &nv, &ne, &vtx, &edges));
116   for (i = 0; i < nv; i++) {
117     PetscCall(DMNetworkIsGhostVertex(networkdm, vtx[i], &ghostvtex));
118     if (ghostvtex) continue;
119 
120     PetscCall(DMNetworkGetGlobalVecOffset(networkdm, vtx[i], ALL_COMPONENTS, &offset));
121     PetscCall(DMNetworkGetComponent(networkdm, vtx[i], ALL_COMPONENTS, NULL, NULL, &nvar));
122     for (j = 0; j < nvar; j++) {
123       row = offset + j;
124       PetscCall(MatSetValues(J, 1, &row, 1, &row, &one, ADD_VALUES));
125     }
126   }
127   PetscCall(MatAssemblyBegin(J, MAT_FINAL_ASSEMBLY));
128   PetscCall(MatAssemblyEnd(J, MAT_FINAL_ASSEMBLY));
129 
130   PetscCall(DMRestoreLocalVector(networkdm, &localX));
131   PetscFunctionReturn(PETSC_SUCCESS);
132 }
133 
134 /* Dummy equation localF(X) = localX - localXold */
135 PetscErrorCode FormFunction_Dummy(DM networkdm, Vec localX, Vec localF, PetscInt nv, PetscInt ne, const PetscInt *vtx, const PetscInt *edges, void *appctx)
136 {
137   const PetscScalar *xarr, *xoldarr;
138   PetscScalar       *farr;
139   PetscInt           i, j, offset, nvar;
140   PetscBool          ghostvtex;
141   UserCtx           *user      = (UserCtx *)appctx;
142   Vec                localXold = user->localXold;
143 
144   PetscFunctionBegin;
145   PetscCall(VecGetArrayRead(localX, &xarr));
146   PetscCall(VecGetArrayRead(localXold, &xoldarr));
147   PetscCall(VecGetArray(localF, &farr));
148 
149   for (i = 0; i < nv; i++) {
150     PetscCall(DMNetworkIsGhostVertex(networkdm, vtx[i], &ghostvtex));
151     if (ghostvtex) continue;
152 
153     PetscCall(DMNetworkGetLocalVecOffset(networkdm, vtx[i], ALL_COMPONENTS, &offset));
154     PetscCall(DMNetworkGetComponent(networkdm, vtx[i], ALL_COMPONENTS, NULL, NULL, &nvar));
155     for (j = 0; j < nvar; j++) farr[offset + j] = xarr[offset + j] - xoldarr[offset + j];
156   }
157 
158   PetscCall(VecRestoreArrayRead(localX, &xarr));
159   PetscCall(VecRestoreArrayRead(localXold, &xoldarr));
160   PetscCall(VecRestoreArray(localF, &farr));
161   PetscFunctionReturn(PETSC_SUCCESS);
162 }
163 
164 PetscErrorCode FormFunction(SNES snes, Vec X, Vec F, void *appctx)
165 {
166   DM              networkdm;
167   Vec             localX, localF;
168   PetscInt        nv, ne, v;
169   const PetscInt *vtx, *edges;
170   PetscMPIInt     rank;
171   MPI_Comm        comm;
172   UserCtx        *user         = (UserCtx *)appctx;
173   UserCtx_Power   appctx_power = (*user).appctx_power;
174   AppCtx_Water    appctx_water = (*user).appctx_water;
175 
176   PetscFunctionBegin;
177   PetscCall(SNESGetDM(snes, &networkdm));
178   PetscCall(PetscObjectGetComm((PetscObject)networkdm, &comm));
179   PetscCallMPI(MPI_Comm_rank(comm, &rank));
180 
181   PetscCall(DMGetLocalVector(networkdm, &localX));
182   PetscCall(DMGetLocalVector(networkdm, &localF));
183   PetscCall(VecSet(F, 0.0));
184   PetscCall(VecSet(localF, 0.0));
185 
186   PetscCall(DMGlobalToLocalBegin(networkdm, X, INSERT_VALUES, localX));
187   PetscCall(DMGlobalToLocalEnd(networkdm, X, INSERT_VALUES, localX));
188 
189   /* Form Function for power subnetwork */
190   PetscCall(DMNetworkGetSubnetwork(networkdm, 0, &nv, &ne, &vtx, &edges));
191   if (user->subsnes_id == 1) { /* snes_water only */
192     PetscCall(FormFunction_Dummy(networkdm, localX, localF, nv, ne, vtx, edges, user));
193   } else {
194     PetscCall(FormFunction_Power(networkdm, localX, localF, nv, ne, vtx, edges, &appctx_power));
195   }
196 
197   /* Form Function for water subnetwork */
198   PetscCall(DMNetworkGetSubnetwork(networkdm, 1, &nv, &ne, &vtx, &edges));
199   if (user->subsnes_id == 0) { /* snes_power only */
200     PetscCall(FormFunction_Dummy(networkdm, localX, localF, nv, ne, vtx, edges, user));
201   } else {
202     PetscCall(FormFunction_Water(networkdm, localX, localF, nv, ne, vtx, edges, NULL));
203   }
204 
205   /* Illustrate how to access the coupling vertex of the subnetworks without doing anything to F yet */
206   PetscCall(DMNetworkGetSharedVertices(networkdm, &nv, &vtx));
207   for (v = 0; v < nv; v++) {
208     PetscInt        key, ncomp, nvar, nconnedges, k, e, keye, goffset[3];
209     void           *component;
210     const PetscInt *connedges;
211 
212     PetscCall(DMNetworkGetComponent(networkdm, vtx[v], ALL_COMPONENTS, NULL, NULL, &nvar));
213     PetscCall(DMNetworkGetNumComponents(networkdm, vtx[v], &ncomp));
214     /* printf("  [%d] coupling vertex[%" PetscInt_FMT "]: v %" PetscInt_FMT ", ncomp %" PetscInt_FMT "; nvar %" PetscInt_FMT "\n",rank,v,vtx[v], ncomp,nvar); */
215 
216     for (k = 0; k < ncomp; k++) {
217       PetscCall(DMNetworkGetComponent(networkdm, vtx[v], k, &key, &component, &nvar));
218       PetscCall(DMNetworkGetGlobalVecOffset(networkdm, vtx[v], k, &goffset[k]));
219 
220       /* Verify the coupling vertex is a powernet load vertex or a water vertex */
221       switch (k) {
222       case 0:
223         PetscCheck(key == appctx_power.compkey_bus && nvar == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "key %" PetscInt_FMT " not a power bus vertex or nvar %" PetscInt_FMT " != 2", key, nvar);
224         break;
225       case 1:
226         PetscCheck(key == appctx_power.compkey_load && nvar == 0 && goffset[1] == goffset[0] + 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Not a power load vertex");
227         break;
228       case 2:
229         PetscCheck(key == appctx_water.compkey_vtx && nvar == 1 && goffset[2] == goffset[1], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Not a water vertex");
230         break;
231       default:
232         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "k %" PetscInt_FMT " is wrong", k);
233       }
234       /* printf("  [%d] coupling vertex[%" PetscInt_FMT "]: key %" PetscInt_FMT "; nvar %" PetscInt_FMT ", goffset %" PetscInt_FMT "\n",rank,v,key,nvar,goffset[k]); */
235     }
236 
237     /* Get its supporting edges */
238     PetscCall(DMNetworkGetSupportingEdges(networkdm, vtx[v], &nconnedges, &connedges));
239     /* printf("\n[%d] coupling vertex: nconnedges %" PetscInt_FMT "\n",rank,nconnedges); */
240     for (k = 0; k < nconnedges; k++) {
241       e = connedges[k];
242       PetscCall(DMNetworkGetNumComponents(networkdm, e, &ncomp));
243       /* printf("\n  [%d] connected edge[%" PetscInt_FMT "]=%" PetscInt_FMT " has ncomp %" PetscInt_FMT "\n",rank,k,e,ncomp); */
244       PetscCall(DMNetworkGetComponent(networkdm, e, 0, &keye, &component, NULL));
245       if (keye != appctx_water.compkey_edge) PetscCheck(keye == appctx_power.compkey_branch, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Not a power branch");
246     }
247   }
248 
249   PetscCall(DMRestoreLocalVector(networkdm, &localX));
250 
251   PetscCall(DMLocalToGlobalBegin(networkdm, localF, ADD_VALUES, F));
252   PetscCall(DMLocalToGlobalEnd(networkdm, localF, ADD_VALUES, F));
253   PetscCall(DMRestoreLocalVector(networkdm, &localF));
254 #if 0
255   if (rank == 0) printf("F:\n");
256   PetscCall(VecView(F,PETSC_VIEWER_STDOUT_WORLD));
257 #endif
258   PetscFunctionReturn(PETSC_SUCCESS);
259 }
260 
261 PetscErrorCode SetInitialGuess(DM networkdm, Vec X, void *appctx)
262 {
263   PetscInt        nv, ne, i, j, ncomp, offset, key;
264   const PetscInt *vtx, *edges;
265   UserCtx        *user         = (UserCtx *)appctx;
266   Vec             localX       = user->localXold;
267   UserCtx_Power   appctx_power = (*user).appctx_power;
268   AppCtx_Water    appctx_water = (*user).appctx_water;
269   PetscBool       ghost;
270   PetscScalar    *xarr;
271   VERTEX_Power    bus;
272   VERTEX_Water    vertex;
273   void           *component;
274   GEN             gen;
275 
276   PetscFunctionBegin;
277   PetscCall(VecSet(X, 0.0));
278   PetscCall(VecSet(localX, 0.0));
279 
280   /* Set initial guess for power subnetwork */
281   PetscCall(DMNetworkGetSubnetwork(networkdm, 0, &nv, &ne, &vtx, &edges));
282   PetscCall(SetInitialGuess_Power(networkdm, localX, nv, ne, vtx, edges, &appctx_power));
283 
284   /* Set initial guess for water subnetwork */
285   PetscCall(DMNetworkGetSubnetwork(networkdm, 1, &nv, &ne, &vtx, &edges));
286   PetscCall(SetInitialGuess_Water(networkdm, localX, nv, ne, vtx, edges, NULL));
287 
288   /* Set initial guess at the coupling vertex */
289   PetscCall(VecGetArray(localX, &xarr));
290   PetscCall(DMNetworkGetSharedVertices(networkdm, &nv, &vtx));
291   for (i = 0; i < nv; i++) {
292     PetscCall(DMNetworkIsGhostVertex(networkdm, vtx[i], &ghost));
293     if (ghost) continue;
294 
295     PetscCall(DMNetworkGetNumComponents(networkdm, vtx[i], &ncomp));
296     for (j = 0; j < ncomp; j++) {
297       PetscCall(DMNetworkGetLocalVecOffset(networkdm, vtx[i], j, &offset));
298       PetscCall(DMNetworkGetComponent(networkdm, vtx[i], j, &key, (void **)&component, NULL));
299       if (key == appctx_power.compkey_bus) {
300         bus              = (VERTEX_Power)(component);
301         xarr[offset]     = bus->va * PETSC_PI / 180.0;
302         xarr[offset + 1] = bus->vm;
303       } else if (key == appctx_power.compkey_gen) {
304         gen = (GEN)(component);
305         if (!gen->status) continue;
306         xarr[offset + 1] = gen->vs;
307       } else if (key == appctx_water.compkey_vtx) {
308         vertex = (VERTEX_Water)(component);
309         if (vertex->type == VERTEX_TYPE_JUNCTION) {
310           xarr[offset] = 100;
311         } else if (vertex->type == VERTEX_TYPE_RESERVOIR) {
312           xarr[offset] = vertex->res.head;
313         } else {
314           xarr[offset] = vertex->tank.initlvl + vertex->tank.elev;
315         }
316       }
317     }
318   }
319   PetscCall(VecRestoreArray(localX, &xarr));
320 
321   PetscCall(DMLocalToGlobalBegin(networkdm, localX, ADD_VALUES, X));
322   PetscCall(DMLocalToGlobalEnd(networkdm, localX, ADD_VALUES, X));
323   PetscFunctionReturn(PETSC_SUCCESS);
324 }
325 
326 /* Set coordinates */
327 static PetscErrorCode CoordinateVecSetUp(DM dmcoords, Vec coords)
328 {
329   PetscInt        i, gidx, offset, v, nv, Nsubnet;
330   const PetscInt *vtx;
331   PetscScalar    *carray;
332   PetscReal      *color;
333 
334   PetscFunctionBeginUser;
335   PetscCall(VecGetArrayWrite(coords, &carray));
336   PetscCall(DMNetworkGetNumSubNetworks(dmcoords, NULL, &Nsubnet));
337   for (i = 0; i < Nsubnet; i++) {
338     PetscCall(DMNetworkGetSubnetwork(dmcoords, i, &nv, NULL, &vtx, NULL));
339     for (v = 0; v < nv; v++) {
340       PetscCall(DMNetworkGetGlobalVertexIndex(dmcoords, vtx[v], &gidx));
341       PetscCall(DMNetworkGetLocalVecOffset(dmcoords, vtx[v], 0, &offset));
342       PetscCall(DMNetworkGetComponent(dmcoords, vtx[v], 0, NULL, (void **)&color, NULL));
343       *color = 0.0;
344       switch (gidx) {
345       case 0:
346         carray[offset]     = -1.0;
347         carray[offset + 1] = -1.0;
348         break;
349       case 1:
350         carray[offset]     = -2.0;
351         carray[offset + 1] = 2.0;
352         break;
353       case 2:
354         carray[offset]     = 0.0;
355         carray[offset + 1] = 2.0;
356         break;
357       case 3:
358         carray[offset]     = -1.0;
359         carray[offset + 1] = 0.0;
360         break;
361       case 4:
362         carray[offset]     = 0.0;
363         carray[offset + 1] = 0.0;
364         break;
365       case 5:
366         carray[offset]     = 0.0;
367         carray[offset + 1] = 1.0;
368         break;
369       case 6:
370         carray[offset]     = -1.0;
371         carray[offset + 1] = 1.0;
372         break;
373       case 7:
374         carray[offset]     = -2.0;
375         carray[offset + 1] = 1.0;
376         break;
377       case 8:
378         carray[offset]     = -2.0;
379         carray[offset + 1] = 0.0;
380         break;
381       case 9:
382         carray[offset]     = 1.0;
383         carray[offset + 1] = 0.0;
384         break;
385       case 10:
386         carray[offset]     = 1.0;
387         carray[offset + 1] = -1.0;
388         break;
389       case 11:
390         carray[offset]     = 2.0;
391         carray[offset + 1] = -1.0;
392         break;
393       case 12:
394         carray[offset]     = 2.0;
395         carray[offset + 1] = 0.0;
396         break;
397       case 13:
398         carray[offset]     = 0.0;
399         carray[offset + 1] = -1.0;
400         break;
401       case 14:
402         carray[offset]     = 2.0;
403         carray[offset + 1] = 1.0;
404         break;
405       default:
406         PetscCheck(gidx < 15 && gidx > -1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "gidx %" PetscInt_FMT "must between 0 and 14", gidx);
407       }
408     }
409   }
410   PetscCall(VecRestoreArrayWrite(coords, &carray));
411   PetscFunctionReturn(PETSC_SUCCESS);
412 }
413 
414 static PetscErrorCode CoordinatePrint(DM dm)
415 {
416   DM                 dmcoords;
417   PetscInt           cdim, v, off, vglobal, vStart, vEnd;
418   const PetscScalar *carray;
419   Vec                coords;
420   MPI_Comm           comm;
421   PetscMPIInt        rank;
422 
423   PetscFunctionBegin;
424   /* get info from dm */
425   PetscCall(DMGetCoordinateDim(dm, &cdim));
426   PetscCall(DMGetCoordinatesLocal(dm, &coords));
427 
428   PetscCall(DMGetCoordinateDM(dm, &dmcoords));
429   PetscCall(PetscObjectGetComm((PetscObject)dmcoords, &comm));
430   PetscCallMPI(MPI_Comm_rank(comm, &rank));
431 
432   /* print coordinates from dmcoords */
433   PetscCall(PetscPrintf(MPI_COMM_WORLD, "\nCoordinatePrint, cdim %" PetscInt_FMT ":\n", cdim));
434   PetscCall(PetscSynchronizedPrintf(MPI_COMM_WORLD, "[%d]\n", rank));
435 
436   PetscCall(DMNetworkGetVertexRange(dmcoords, &vStart, &vEnd));
437   PetscCall(VecGetArrayRead(coords, &carray));
438   for (v = vStart; v < vEnd; v++) {
439     PetscCall(DMNetworkGetLocalVecOffset(dmcoords, v, 0, &off));
440     PetscCall(DMNetworkGetGlobalVertexIndex(dmcoords, v, &vglobal));
441     switch (cdim) {
442     case 2:
443       PetscCall(PetscSynchronizedPrintf(MPI_COMM_WORLD, "Vertex: %" PetscInt_FMT ", x =  %f y = %f \n", vglobal, (double)PetscRealPart(carray[off]), (double)PetscRealPart(carray[off + 1])));
444       break;
445     default:
446       PetscCheck(cdim == 2, MPI_COMM_WORLD, PETSC_ERR_SUP, "Only supports Network embedding dimension of 2, not supplied  %" PetscInt_FMT, cdim);
447       break;
448     }
449   }
450   PetscCall(PetscSynchronizedFlush(MPI_COMM_WORLD, NULL));
451   PetscCall(VecRestoreArrayRead(coords, &carray));
452   PetscFunctionReturn(PETSC_SUCCESS);
453 }
454 
455 int main(int argc, char **argv)
456 {
457   DM                  networkdm, dmcoords;
458   PetscMPIInt         rank, size;
459   PetscInt            Nsubnet = 2, numVertices[2], numEdges[2], i, j, nv, ne, it_max = 10;
460   PetscInt            vStart, vEnd, compkey;
461   const PetscInt     *vtx, *edges;
462   PetscReal          *color;
463   Vec                 X, F, coords;
464   SNES                snes, snes_power, snes_water;
465   Mat                 Jac;
466   PetscBool           ghost, viewJ = PETSC_FALSE, viewX = PETSC_FALSE, test = PETSC_FALSE, distribute = PETSC_TRUE, flg, printCoord = PETSC_FALSE, viewCSV = PETSC_FALSE, monitorIt = PETSC_FALSE;
467   UserCtx             user;
468   SNESConvergedReason reason;
469 #if defined(PETSC_USE_LOG)
470   PetscLogStage stage[4];
471 #endif
472 
473   /* Power subnetwork */
474   UserCtx_Power *appctx_power                    = &user.appctx_power;
475   char           pfdata_file[PETSC_MAX_PATH_LEN] = "power/case9.m";
476   PFDATA        *pfdata                          = NULL;
477   PetscInt       genj, loadj, *edgelist_power = NULL, power_netnum;
478   PetscScalar    Sbase = 0.0;
479 
480   /* Water subnetwork */
481   AppCtx_Water *appctx_water                       = &user.appctx_water;
482   WATERDATA    *waterdata                          = NULL;
483   char          waterdata_file[PETSC_MAX_PATH_LEN] = "water/sample1.inp";
484   PetscInt     *edgelist_water                     = NULL, water_netnum;
485 
486   /* Shared vertices between subnetworks */
487   PetscInt power_svtx, water_svtx;
488 
489   PetscFunctionBeginUser;
490   PetscCall(PetscInitialize(&argc, &argv, "ex1options", help));
491   PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD, &rank));
492   PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
493 
494   /* (1) Read Data - Only rank 0 reads the data */
495   PetscCall(PetscLogStageRegister("Read Data", &stage[0]));
496   PetscCall(PetscLogStagePush(stage[0]));
497 
498   for (i = 0; i < Nsubnet; i++) {
499     numVertices[i] = 0;
500     numEdges[i]    = 0;
501   }
502 
503   /* All processes READ THE DATA FOR THE FIRST SUBNETWORK: Electric Power Grid */
504   /* Used for shared vertex, because currently the coupling info must be available in all processes!!! */
505   PetscCall(PetscOptionsGetString(NULL, NULL, "-pfdata", pfdata_file, PETSC_MAX_PATH_LEN - 1, NULL));
506   PetscCall(PetscNew(&pfdata));
507   PetscCall(PFReadMatPowerData(pfdata, pfdata_file));
508   if (rank == 0) PetscCall(PetscPrintf(PETSC_COMM_SELF, "Power network: nb = %" PetscInt_FMT ", ngen = %" PetscInt_FMT ", nload = %" PetscInt_FMT ", nbranch = %" PetscInt_FMT "\n", pfdata->nbus, pfdata->ngen, pfdata->nload, pfdata->nbranch));
509   Sbase = pfdata->sbase;
510   if (rank == 0) { /* proc[0] will create Electric Power Grid */
511     numEdges[0]    = pfdata->nbranch;
512     numVertices[0] = pfdata->nbus;
513 
514     PetscCall(PetscMalloc1(2 * numEdges[0], &edgelist_power));
515     PetscCall(GetListofEdges_Power(pfdata, edgelist_power));
516   }
517   /* Broadcast power Sbase to all processors */
518   PetscCallMPI(MPI_Bcast(&Sbase, 1, MPIU_SCALAR, 0, PETSC_COMM_WORLD));
519   appctx_power->Sbase     = Sbase;
520   appctx_power->jac_error = PETSC_FALSE;
521   /* If external option activated. Introduce error in jacobian */
522   PetscCall(PetscOptionsHasName(NULL, NULL, "-jac_error", &appctx_power->jac_error));
523 
524   /* All processes READ THE DATA FOR THE SECOND SUBNETWORK: Water */
525   /* Used for shared vertex, because currently the coupling info must be available in all processes!!! */
526   PetscCall(PetscNew(&waterdata));
527   PetscCall(PetscOptionsGetString(NULL, NULL, "-waterdata", waterdata_file, PETSC_MAX_PATH_LEN - 1, NULL));
528   PetscCall(WaterReadData(waterdata, waterdata_file));
529   if (size == 1 || (size > 1 && rank == 1)) {
530     PetscCall(PetscCalloc1(2 * waterdata->nedge, &edgelist_water));
531     PetscCall(GetListofEdges_Water(waterdata, edgelist_water));
532     numEdges[1]    = waterdata->nedge;
533     numVertices[1] = waterdata->nvertex;
534   }
535   PetscCall(PetscLogStagePop());
536 
537   /* (2) Create a network consist of two subnetworks */
538   PetscCall(PetscLogStageRegister("Net Setup", &stage[1]));
539   PetscCall(PetscLogStagePush(stage[1]));
540 
541   PetscCall(PetscOptionsGetBool(NULL, NULL, "-viewCSV", &viewCSV, NULL));
542   PetscCall(PetscOptionsGetBool(NULL, NULL, "-printCoord", &printCoord, NULL));
543   PetscCall(PetscOptionsGetBool(NULL, NULL, "-test", &test, NULL));
544   PetscCall(PetscOptionsGetBool(NULL, NULL, "-distribute", &distribute, NULL));
545 
546   /* Create an empty network object */
547   PetscCall(DMNetworkCreate(PETSC_COMM_WORLD, &networkdm));
548 
549   /* Register the components in the network */
550   PetscCall(DMNetworkRegisterComponent(networkdm, "branchstruct", sizeof(struct _p_EDGE_Power), &appctx_power->compkey_branch));
551   PetscCall(DMNetworkRegisterComponent(networkdm, "busstruct", sizeof(struct _p_VERTEX_Power), &appctx_power->compkey_bus));
552   PetscCall(DMNetworkRegisterComponent(networkdm, "genstruct", sizeof(struct _p_GEN), &appctx_power->compkey_gen));
553   PetscCall(DMNetworkRegisterComponent(networkdm, "loadstruct", sizeof(struct _p_LOAD), &appctx_power->compkey_load));
554 
555   PetscCall(DMNetworkRegisterComponent(networkdm, "edge_water", sizeof(struct _p_EDGE_Water), &appctx_water->compkey_edge));
556   PetscCall(DMNetworkRegisterComponent(networkdm, "vertex_water", sizeof(struct _p_VERTEX_Water), &appctx_water->compkey_vtx));
557 
558   PetscCall(PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[%d] Total local nvertices %" PetscInt_FMT " + %" PetscInt_FMT " = %" PetscInt_FMT ", nedges %" PetscInt_FMT " + %" PetscInt_FMT " = %" PetscInt_FMT "\n", rank, numVertices[0], numVertices[1], numVertices[0] + numVertices[1], numEdges[0], numEdges[1], numEdges[0] + numEdges[1]));
559   PetscCall(PetscSynchronizedFlush(PETSC_COMM_WORLD, PETSC_STDOUT));
560 
561   PetscCall(DMNetworkSetNumSubNetworks(networkdm, PETSC_DECIDE, Nsubnet));
562   PetscCall(DMNetworkAddSubnetwork(networkdm, "power", numEdges[0], edgelist_power, &power_netnum));
563   PetscCall(DMNetworkAddSubnetwork(networkdm, "water", numEdges[1], edgelist_water, &water_netnum));
564 
565   /* vertex subnet[0].4 shares with vertex subnet[1].0 */
566   power_svtx = 4;
567   water_svtx = 0;
568   PetscCall(DMNetworkAddSharedVertices(networkdm, power_netnum, water_netnum, 1, &power_svtx, &water_svtx));
569 
570   /* Set up the network layout */
571   PetscCall(DMNetworkLayoutSetUp(networkdm));
572 
573   /* ADD VARIABLES AND COMPONENTS FOR THE POWER SUBNETWORK */
574   genj  = 0;
575   loadj = 0;
576   PetscCall(DMNetworkGetSubnetwork(networkdm, power_netnum, &nv, &ne, &vtx, &edges));
577 
578   for (i = 0; i < ne; i++) PetscCall(DMNetworkAddComponent(networkdm, edges[i], appctx_power->compkey_branch, &pfdata->branch[i], 0));
579 
580   for (i = 0; i < nv; i++) {
581     PetscCall(DMNetworkIsSharedVertex(networkdm, vtx[i], &flg));
582     if (flg) continue;
583 
584     PetscCall(DMNetworkAddComponent(networkdm, vtx[i], appctx_power->compkey_bus, &pfdata->bus[i], 2));
585     if (pfdata->bus[i].ngen) {
586       for (j = 0; j < pfdata->bus[i].ngen; j++) PetscCall(DMNetworkAddComponent(networkdm, vtx[i], appctx_power->compkey_gen, &pfdata->gen[genj++], 0));
587     }
588     if (pfdata->bus[i].nload) {
589       for (j = 0; j < pfdata->bus[i].nload; j++) PetscCall(DMNetworkAddComponent(networkdm, vtx[i], appctx_power->compkey_load, &pfdata->load[loadj++], 0));
590     }
591   }
592 
593   /* ADD VARIABLES AND COMPONENTS FOR THE WATER SUBNETWORK */
594   PetscCall(DMNetworkGetSubnetwork(networkdm, water_netnum, &nv, &ne, &vtx, &edges));
595   for (i = 0; i < ne; i++) PetscCall(DMNetworkAddComponent(networkdm, edges[i], appctx_water->compkey_edge, &waterdata->edge[i], 0));
596 
597   for (i = 0; i < nv; i++) {
598     PetscCall(DMNetworkIsSharedVertex(networkdm, vtx[i], &flg));
599     if (flg) continue;
600 
601     PetscCall(DMNetworkAddComponent(networkdm, vtx[i], appctx_water->compkey_vtx, &waterdata->vertex[i], 1));
602   }
603 
604   /* ADD VARIABLES AND COMPONENTS AT THE SHARED VERTEX: net[0].4 coupls with net[1].0 -- owning and all ghost ranks of the vertex do this */
605   PetscCall(DMNetworkGetSharedVertices(networkdm, &nv, &vtx));
606   for (i = 0; i < nv; i++) {
607     /* power */
608     PetscCall(DMNetworkAddComponent(networkdm, vtx[i], appctx_power->compkey_bus, &pfdata->bus[4], 2));
609     /* bus[4] is a load, add its component */
610     PetscCall(DMNetworkAddComponent(networkdm, vtx[i], appctx_power->compkey_load, &pfdata->load[0], 0));
611 
612     /* water */
613     PetscCall(DMNetworkAddComponent(networkdm, vtx[i], appctx_water->compkey_vtx, &waterdata->vertex[0], 1));
614   }
615 
616   /* Set coordinates for visualization */
617   PetscCall(DMSetCoordinateDim(networkdm, 2));
618   PetscCall(DMGetCoordinateDM(networkdm, &dmcoords));
619   PetscCall(DMNetworkGetVertexRange(dmcoords, &vStart, &vEnd));
620 
621   PetscCall(PetscCalloc1(vEnd - vStart, &color));
622   PetscCall(DMNetworkRegisterComponent(dmcoords, "coordinate&color", sizeof(PetscReal), &compkey));
623   for (i = vStart; i < vEnd; i++) PetscCall(DMNetworkAddComponent(dmcoords, i, compkey, &color[i - vStart], 2));
624   PetscCall(DMNetworkFinalizeComponents(dmcoords));
625 
626   PetscCall(DMCreateLocalVector(dmcoords, &coords));
627   PetscCall(DMSetCoordinatesLocal(networkdm, coords)); /* set/get coords to/from networkdm */
628   PetscCall(CoordinateVecSetUp(dmcoords, coords));
629   if (printCoord) PetscCall(CoordinatePrint(networkdm));
630 
631   /* Set up DM for use */
632   PetscCall(DMSetUp(networkdm));
633 
634   /* Free user objects */
635   PetscCall(PetscFree(edgelist_power));
636   PetscCall(PetscFree(pfdata->bus));
637   PetscCall(PetscFree(pfdata->gen));
638   PetscCall(PetscFree(pfdata->branch));
639   PetscCall(PetscFree(pfdata->load));
640   PetscCall(PetscFree(pfdata));
641 
642   PetscCall(PetscFree(edgelist_water));
643   PetscCall(PetscFree(waterdata->vertex));
644   PetscCall(PetscFree(waterdata->edge));
645   PetscCall(PetscFree(waterdata));
646 
647   /* Re-distribute networkdm to multiple processes for better job balance */
648   if (distribute) {
649     PetscCall(DMNetworkDistribute(&networkdm, 0));
650 
651     if (printCoord) PetscCall(CoordinatePrint(networkdm));
652     if (viewCSV) { /* CSV View of network with coordinates */
653       PetscCall(PetscViewerPushFormat(PETSC_VIEWER_STDOUT_WORLD, PETSC_VIEWER_ASCII_CSV));
654       PetscCall(DMView(networkdm, PETSC_VIEWER_STDOUT_WORLD));
655       PetscCall(PetscViewerPopFormat(PETSC_VIEWER_STDOUT_WORLD));
656     }
657   }
658   PetscCall(VecDestroy(&coords));
659 
660   /* Test DMNetworkGetSubnetwork() and DMNetworkGetSubnetworkSharedVertices() */
661   if (test) {
662     PetscInt v, gidx;
663     PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
664     for (i = 0; i < Nsubnet; i++) {
665       PetscCall(DMNetworkGetSubnetwork(networkdm, i, &nv, &ne, &vtx, &edges));
666       PetscCall(PetscPrintf(PETSC_COMM_SELF, "[%d] After distribute, subnet[%" PetscInt_FMT "] ne %" PetscInt_FMT ", nv %" PetscInt_FMT "\n", rank, i, ne, nv));
667       PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
668 
669       for (v = 0; v < nv; v++) {
670         PetscCall(DMNetworkIsGhostVertex(networkdm, vtx[v], &ghost));
671         PetscCall(DMNetworkGetGlobalVertexIndex(networkdm, vtx[v], &gidx));
672         PetscCall(PetscPrintf(PETSC_COMM_SELF, "[%d] subnet[%" PetscInt_FMT "] v %" PetscInt_FMT " %" PetscInt_FMT "; ghost %d\n", rank, i, vtx[v], gidx, ghost));
673       }
674       PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
675     }
676     PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
677 
678     PetscCall(DMNetworkGetSharedVertices(networkdm, &nv, &vtx));
679     PetscCall(PetscPrintf(PETSC_COMM_SELF, "[%d] After distribute, num of shared vertices nsv = %" PetscInt_FMT "\n", rank, nv));
680     for (v = 0; v < nv; v++) {
681       PetscCall(DMNetworkGetGlobalVertexIndex(networkdm, vtx[v], &gidx));
682       PetscCall(PetscPrintf(PETSC_COMM_SELF, "[%d] sv %" PetscInt_FMT ", gidx=%" PetscInt_FMT "\n", rank, vtx[v], gidx));
683     }
684     PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
685   }
686 
687   /* Create solution vector X */
688   PetscCall(DMCreateGlobalVector(networkdm, &X));
689   PetscCall(VecDuplicate(X, &F));
690   PetscCall(DMGetLocalVector(networkdm, &user.localXold));
691   PetscCall(PetscLogStagePop());
692 
693   /* (3) Setup Solvers */
694   PetscCall(PetscOptionsGetBool(NULL, NULL, "-viewJ", &viewJ, NULL));
695   PetscCall(PetscOptionsGetBool(NULL, NULL, "-viewX", &viewX, NULL));
696 
697   PetscCall(PetscLogStageRegister("SNES Setup", &stage[2]));
698   PetscCall(PetscLogStagePush(stage[2]));
699 
700   PetscCall(SetInitialGuess(networkdm, X, &user));
701 
702   /* Create coupled snes */
703   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "SNES_coupled setup ......\n"));
704   user.subsnes_id = Nsubnet;
705   PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));
706   PetscCall(SNESSetDM(snes, networkdm));
707   PetscCall(SNESSetOptionsPrefix(snes, "coupled_"));
708   PetscCall(SNESSetFunction(snes, F, FormFunction, &user));
709   /* set maxit=1 which can be changed via option '-coupled_snes_max_it <>', see ex1options */
710   PetscCall(SNESSetTolerances(snes, PETSC_DEFAULT, PETSC_DEFAULT, PETSC_DEFAULT, 1, PETSC_DEFAULT));
711   PetscCall(SNESSetFromOptions(snes));
712 
713   if (viewJ) {
714     /* View Jac structure */
715     PetscCall(SNESGetJacobian(snes, &Jac, NULL, NULL, NULL));
716     PetscCall(MatView(Jac, PETSC_VIEWER_DRAW_WORLD));
717   }
718 
719   if (viewX) {
720     PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Solution:\n"));
721     PetscCall(VecView(X, PETSC_VIEWER_STDOUT_WORLD));
722   }
723 
724   if (viewJ) {
725     /* View assembled Jac */
726     PetscCall(MatView(Jac, PETSC_VIEWER_DRAW_WORLD));
727   }
728 
729   /* Create snes_power */
730   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "SNES_power setup ......\n"));
731   user.subsnes_id = 0;
732   PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes_power));
733   PetscCall(SNESSetDM(snes_power, networkdm));
734   PetscCall(SNESSetOptionsPrefix(snes_power, "power_"));
735   PetscCall(SNESSetFunction(snes_power, F, FormFunction, &user));
736   /* set maxit=1 which can be changed via option '-power_snes_max_it <>', see ex1options */
737   PetscCall(SNESSetTolerances(snes_power, PETSC_DEFAULT, PETSC_DEFAULT, PETSC_DEFAULT, 1, PETSC_DEFAULT));
738 
739   /* Use user-provide Jacobian */
740   PetscCall(DMCreateMatrix(networkdm, &Jac));
741   PetscCall(SNESSetJacobian(snes_power, Jac, Jac, FormJacobian_subPower, &user));
742   PetscCall(SNESSetFromOptions(snes_power));
743 
744   if (viewX) {
745     PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Power Solution:\n"));
746     PetscCall(VecView(X, PETSC_VIEWER_STDOUT_WORLD));
747   }
748   if (viewJ) {
749     PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Power Jac:\n"));
750     PetscCall(SNESGetJacobian(snes_power, &Jac, NULL, NULL, NULL));
751     PetscCall(MatView(Jac, PETSC_VIEWER_DRAW_WORLD));
752   }
753 
754   /* Create snes_water */
755   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "SNES_water setup......\n"));
756 
757   user.subsnes_id = 1;
758   PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes_water));
759   PetscCall(SNESSetDM(snes_water, networkdm));
760   PetscCall(SNESSetOptionsPrefix(snes_water, "water_"));
761   PetscCall(SNESSetFunction(snes_water, F, FormFunction, &user));
762   /* set maxit=1 which can be changed via option '-water_snes_max_it <>', see ex1options */
763   PetscCall(SNESSetTolerances(snes_water, PETSC_DEFAULT, PETSC_DEFAULT, PETSC_DEFAULT, 1, PETSC_DEFAULT));
764   PetscCall(SNESSetFromOptions(snes_water));
765 
766   if (viewX) {
767     PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Water Solution:\n"));
768     PetscCall(VecView(X, PETSC_VIEWER_STDOUT_WORLD));
769   }
770 
771   /* Monitor snes, snes_power and snes_water iterations */
772   PetscCall(PetscOptionsGetBool(NULL, NULL, "-monitorIteration", &monitorIt, NULL));
773   user.monitorColor = PETSC_FALSE;
774   PetscCall(PetscOptionsGetBool(NULL, NULL, "-monitorColor", &user.monitorColor, NULL));
775   if (user.monitorColor) monitorIt = PETSC_TRUE; /* require installation of pandas and matplotlib */
776   if (monitorIt) {
777     PetscCall(SNESMonitorSet(snes_power, UserMonitor, &user, NULL));
778     PetscCall(SNESMonitorSet(snes_water, UserMonitor, &user, NULL));
779     PetscCall(SNESMonitorSet(snes, UserMonitor, &user, NULL));
780   }
781   PetscCall(PetscLogStagePop());
782 
783   /* (4) Solve: we must update user.localXold after each call of SNESSolve().
784          See "PETSc DMNetwork: A Library for Scalable Network PDE-Based Multiphysics Simulations",
785          https://dl.acm.org/doi/10.1145/3344587
786   */
787   PetscCall(PetscLogStageRegister("SNES Solve", &stage[3]));
788   PetscCall(PetscLogStagePush(stage[3]));
789   user.it = 0; /* total_snes_it */
790   reason  = SNES_DIVERGED_DTOL;
791   while (user.it < it_max && (PetscInt)reason < 0) {
792     user.subsnes_id = 0;
793     PetscCall(SNESSolve(snes_power, NULL, X));
794     PetscCall(DMGlobalToLocalBegin(networkdm, X, INSERT_VALUES, user.localXold));
795     PetscCall(DMGlobalToLocalEnd(networkdm, X, INSERT_VALUES, user.localXold));
796 
797     user.subsnes_id = 1;
798     PetscCall(SNESSolve(snes_water, NULL, X));
799     PetscCall(DMGlobalToLocalBegin(networkdm, X, INSERT_VALUES, user.localXold));
800     PetscCall(DMGlobalToLocalEnd(networkdm, X, INSERT_VALUES, user.localXold));
801 
802     user.subsnes_id = Nsubnet;
803     PetscCall(SNESSolve(snes, NULL, X));
804     PetscCall(DMGlobalToLocalBegin(networkdm, X, INSERT_VALUES, user.localXold));
805     PetscCall(DMGlobalToLocalEnd(networkdm, X, INSERT_VALUES, user.localXold));
806 
807     PetscCall(SNESGetConvergedReason(snes, &reason));
808     user.it++;
809   }
810   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Coupled_SNES converged in %" PetscInt_FMT " iterations\n", user.it));
811   if (viewX) {
812     PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Final Solution:\n"));
813     PetscCall(VecView(X, PETSC_VIEWER_STDOUT_WORLD));
814   }
815   PetscCall(PetscLogStagePop());
816 
817   /* Free objects */
818   /* -------------*/
819   PetscCall(PetscFree(color));
820   PetscCall(VecDestroy(&X));
821   PetscCall(VecDestroy(&F));
822   PetscCall(DMRestoreLocalVector(networkdm, &user.localXold));
823 
824   PetscCall(SNESDestroy(&snes));
825   PetscCall(MatDestroy(&Jac));
826   PetscCall(SNESDestroy(&snes_power));
827   PetscCall(SNESDestroy(&snes_water));
828 
829   PetscCall(DMDestroy(&networkdm));
830   PetscCall(PetscFinalize());
831   return 0;
832 }
833 
834 /*TEST
835 
836    build:
837      requires: !complex double defined(PETSC_HAVE_ATTRIBUTEALIGNED)
838      depends: power/PFReadData.c power/pffunctions.c water/waterreaddata.c water/waterfunctions.c
839 
840    test:
841       args: -options_left no -dmnetwork_view -fp_trap 0
842       localrunfiles: ex1options power/case9.m water/sample1.inp
843       output_file: output/ex1.out
844 
845    test:
846       suffix: 2
847       nsize: 3
848       args: -options_left no -petscpartitioner_type parmetis -fp_trap 0
849       localrunfiles: ex1options power/case9.m water/sample1.inp
850       output_file: output/ex1_2.out
851       requires: parmetis
852 
853    test:
854       suffix: 3
855       nsize: 3
856       args: -options_left no -distribute false -fp_trap 0
857       localrunfiles: ex1options power/case9.m water/sample1.inp
858       output_file: output/ex1_2.out
859 
860    test:
861       suffix: 4
862       nsize: 4
863       args: -options_left no -petscpartitioner_type simple -dmnetwork_view -dmnetwork_view_distributed -fp_trap 0
864       localrunfiles: ex1options power/case9.m water/sample1.inp
865       output_file: output/ex1_4.out
866 
867    test:
868       suffix: 5
869       args: -options_left no -viewCSV -fp_trap 0
870       localrunfiles: ex1options power/case9.m water/sample1.inp
871       output_file: output/ex1_5.out
872 
873    test:
874       suffix: 6
875       nsize: 3
876       args: -options_left no -petscpartitioner_type parmetis -dmnetwork_view_distributed draw:null -fp_trap 0
877       localrunfiles: ex1options power/case9.m water/sample1.inp
878       output_file: output/ex1_2.out
879       requires: parmetis
880 
881 TEST*/
882