xref: /petsc/src/snes/tutorials/network/ex1.c (revision e08b1d6d0faae6eca507e20c9d3498f81719d047)
1 static char help[] = "This example demonstrates the use of DMNetwork interface 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                       This example shows the use of subnetwork feature in DMNetwork. \n\
6                       Run this program: mpiexec -n <n> ./ex1 \n\\n";
7 
8 #include "power/power.h"
9 #include "water/water.h"
10 
11 typedef struct{
12   UserCtx_Power appctx_power;
13   AppCtx_Water  appctx_water;
14   PetscInt      subsnes_id; /* snes solver id */
15   PetscInt      it;         /* iteration number */
16   Vec           localXold;  /* store previous solution, used by FormFunction_Dummy() */
17 } UserCtx;
18 
19 PetscErrorCode UserMonitor(SNES snes,PetscInt its,PetscReal fnorm ,void *appctx)
20 {
21   UserCtx        *user = (UserCtx*)appctx;
22   Vec            X,localXold = user->localXold;
23   DM             networkdm;
24   PetscMPIInt    rank;
25   MPI_Comm       comm;
26 
27   PetscFunctionBegin;
28   PetscCall(PetscObjectGetComm((PetscObject)snes,&comm));
29   PetscCallMPI(MPI_Comm_rank(comm,&rank));
30 #if 0
31   if (rank == 0) {
32     PetscInt       subsnes_id = user->subsnes_id;
33     if (subsnes_id == 2) {
34       PetscCall(PetscPrintf(PETSC_COMM_SELF," it %" PetscInt_FMT ", subsnes_id %" PetscInt_FMT ", fnorm %g\n",user->it,user->subsnes_id,(double)fnorm));
35     } else {
36       PetscCall(PetscPrintf(PETSC_COMM_SELF,"       subsnes_id %" PetscInt_FMT ", fnorm %g\n",user->subsnes_id,(double)fnorm));
37     }
38   }
39 #endif
40   PetscCall(SNESGetSolution(snes,&X));
41   PetscCall(SNESGetDM(snes,&networkdm));
42   PetscCall(DMGlobalToLocalBegin(networkdm,X,INSERT_VALUES,localXold));
43   PetscCall(DMGlobalToLocalEnd(networkdm,X,INSERT_VALUES,localXold));
44   PetscFunctionReturn(0);
45 }
46 
47 PetscErrorCode FormJacobian_subPower(SNES snes,Vec X, Mat J,Mat Jpre,void *appctx)
48 {
49   DM             networkdm;
50   Vec            localX;
51   PetscInt       nv,ne,i,j,offset,nvar,row;
52   const PetscInt *vtx,*edges;
53   PetscBool      ghostvtex;
54   PetscScalar    one = 1.0;
55   PetscMPIInt    rank;
56   MPI_Comm       comm;
57 
58   PetscFunctionBegin;
59   PetscCall(SNESGetDM(snes,&networkdm));
60   PetscCall(DMGetLocalVector(networkdm,&localX));
61 
62   PetscCall(PetscObjectGetComm((PetscObject)networkdm,&comm));
63   PetscCallMPI(MPI_Comm_rank(comm,&rank));
64 
65   PetscCall(DMGlobalToLocalBegin(networkdm,X,INSERT_VALUES,localX));
66   PetscCall(DMGlobalToLocalEnd(networkdm,X,INSERT_VALUES,localX));
67 
68   PetscCall(MatZeroEntries(J));
69 
70   /* Power subnetwork: copied from power/FormJacobian_Power() */
71   PetscCall(DMNetworkGetSubnetwork(networkdm,0,&nv,&ne,&vtx,&edges));
72   PetscCall(FormJacobian_Power_private(networkdm,localX,J,nv,ne,vtx,edges,appctx));
73 
74   /* Water subnetwork: Identity */
75   PetscCall(DMNetworkGetSubnetwork(networkdm,1,&nv,&ne,&vtx,&edges));
76   for (i=0; i<nv; i++) {
77     PetscCall(DMNetworkIsGhostVertex(networkdm,vtx[i],&ghostvtex));
78     if (ghostvtex) continue;
79 
80     PetscCall(DMNetworkGetGlobalVecOffset(networkdm,vtx[i],ALL_COMPONENTS,&offset));
81     PetscCall(DMNetworkGetComponent(networkdm,vtx[i],ALL_COMPONENTS,NULL,NULL,&nvar));
82     for (j=0; j<nvar; j++) {
83       row = offset + j;
84       PetscCall(MatSetValues(J,1,&row,1,&row,&one,ADD_VALUES));
85     }
86   }
87   PetscCall(MatAssemblyBegin(J,MAT_FINAL_ASSEMBLY));
88   PetscCall(MatAssemblyEnd(J,MAT_FINAL_ASSEMBLY));
89 
90   PetscCall(DMRestoreLocalVector(networkdm,&localX));
91   PetscFunctionReturn(0);
92 }
93 
94 /* Dummy equation localF(X) = localX - localXold */
95 PetscErrorCode FormFunction_Dummy(DM networkdm,Vec localX, Vec localF,PetscInt nv,PetscInt ne,const PetscInt* vtx,const PetscInt* edges,void* appctx)
96 {
97   const PetscScalar *xarr,*xoldarr;
98   PetscScalar       *farr;
99   PetscInt          i,j,offset,nvar;
100   PetscBool         ghostvtex;
101   UserCtx           *user = (UserCtx*)appctx;
102   Vec               localXold = user->localXold;
103 
104   PetscFunctionBegin;
105   PetscCall(VecGetArrayRead(localX,&xarr));
106   PetscCall(VecGetArrayRead(localXold,&xoldarr));
107   PetscCall(VecGetArray(localF,&farr));
108 
109   for (i=0; i<nv; i++) {
110     PetscCall(DMNetworkIsGhostVertex(networkdm,vtx[i],&ghostvtex));
111     if (ghostvtex) continue;
112 
113     PetscCall(DMNetworkGetLocalVecOffset(networkdm,vtx[i],ALL_COMPONENTS,&offset));
114     PetscCall(DMNetworkGetComponent(networkdm,vtx[i],ALL_COMPONENTS,NULL,NULL,&nvar));
115     for (j=0; j<nvar; j++) {
116       farr[offset+j] = xarr[offset+j] - xoldarr[offset+j];
117     }
118   }
119 
120   PetscCall(VecRestoreArrayRead(localX,&xarr));
121   PetscCall(VecRestoreArrayRead(localXold,&xoldarr));
122   PetscCall(VecRestoreArray(localF,&farr));
123   PetscFunctionReturn(0);
124 }
125 
126 PetscErrorCode FormFunction(SNES snes,Vec X,Vec F,void *appctx)
127 {
128   DM             networkdm;
129   Vec            localX,localF;
130   PetscInt       nv,ne,v;
131   const PetscInt *vtx,*edges;
132   PetscMPIInt    rank;
133   MPI_Comm       comm;
134   UserCtx        *user = (UserCtx*)appctx;
135   UserCtx_Power  appctx_power = (*user).appctx_power;
136   AppCtx_Water   appctx_water = (*user).appctx_water;
137 
138   PetscFunctionBegin;
139   PetscCall(SNESGetDM(snes,&networkdm));
140   PetscCall(PetscObjectGetComm((PetscObject)networkdm,&comm));
141   PetscCallMPI(MPI_Comm_rank(comm,&rank));
142 
143   PetscCall(DMGetLocalVector(networkdm,&localX));
144   PetscCall(DMGetLocalVector(networkdm,&localF));
145   PetscCall(VecSet(F,0.0));
146   PetscCall(VecSet(localF,0.0));
147 
148   PetscCall(DMGlobalToLocalBegin(networkdm,X,INSERT_VALUES,localX));
149   PetscCall(DMGlobalToLocalEnd(networkdm,X,INSERT_VALUES,localX));
150 
151   /* Form Function for power subnetwork */
152   PetscCall(DMNetworkGetSubnetwork(networkdm,0,&nv,&ne,&vtx,&edges));
153   if (user->subsnes_id == 1) { /* snes_water only */
154     PetscCall(FormFunction_Dummy(networkdm,localX,localF,nv,ne,vtx,edges,user));
155   } else {
156     PetscCall(FormFunction_Power(networkdm,localX,localF,nv,ne,vtx,edges,&appctx_power));
157   }
158 
159   /* Form Function for water subnetwork */
160   PetscCall(DMNetworkGetSubnetwork(networkdm,1,&nv,&ne,&vtx,&edges));
161   if (user->subsnes_id == 0) { /* snes_power only */
162     PetscCall(FormFunction_Dummy(networkdm,localX,localF,nv,ne,vtx,edges,user));
163   } else {
164     PetscCall(FormFunction_Water(networkdm,localX,localF,nv,ne,vtx,edges,NULL));
165   }
166 
167   /* Illustrate how to access the coupling vertex of the subnetworks without doing anything to F yet */
168   PetscCall(DMNetworkGetSharedVertices(networkdm,&nv,&vtx));
169   for (v=0; v<nv; v++) {
170     PetscInt       key,ncomp,nvar,nconnedges,k,e,keye,goffset[3];
171     void*          component;
172     const PetscInt *connedges;
173 
174     PetscCall(DMNetworkGetComponent(networkdm,vtx[v],ALL_COMPONENTS,NULL,NULL,&nvar));
175     PetscCall(DMNetworkGetNumComponents(networkdm,vtx[v],&ncomp));
176     /* printf("  [%d] coupling vertex[%" PetscInt_FMT "]: v %" PetscInt_FMT ", ncomp %" PetscInt_FMT "; nvar %" PetscInt_FMT "\n",rank,v,vtx[v], ncomp,nvar); */
177 
178     for (k=0; k<ncomp; k++) {
179       PetscCall(DMNetworkGetComponent(networkdm,vtx[v],k,&key,&component,&nvar));
180       PetscCall(DMNetworkGetGlobalVecOffset(networkdm,vtx[v],k,&goffset[k]));
181 
182       /* Verify the coupling vertex is a powernet load vertex or a water vertex */
183       switch (k) {
184       case 0:
185         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);
186         break;
187       case 1:
188         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");
189         break;
190       case 2:
191         PetscCheck(key == appctx_water.compkey_vtx && nvar == 1 && goffset[2] == goffset[1],PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"Not a water vertex");
192         break;
193       default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "k %" PetscInt_FMT " is wrong",k);
194       }
195       /* printf("  [%d] coupling vertex[%" PetscInt_FMT "]: key %" PetscInt_FMT "; nvar %" PetscInt_FMT ", goffset %" PetscInt_FMT "\n",rank,v,key,nvar,goffset[k]); */
196     }
197 
198     /* Get its supporting edges */
199     PetscCall(DMNetworkGetSupportingEdges(networkdm,vtx[v],&nconnedges,&connedges));
200     /* printf("\n[%d] coupling vertex: nconnedges %" PetscInt_FMT "\n",rank,nconnedges); */
201     for (k=0; k<nconnedges; k++) {
202       e = connedges[k];
203       PetscCall(DMNetworkGetNumComponents(networkdm,e,&ncomp));
204       /* printf("\n  [%d] connected edge[%" PetscInt_FMT "]=%" PetscInt_FMT " has ncomp %" PetscInt_FMT "\n",rank,k,e,ncomp); */
205       PetscCall(DMNetworkGetComponent(networkdm,e,0,&keye,&component,NULL));
206       if (keye != appctx_water.compkey_edge) {
207         PetscCheck(keye == appctx_power.compkey_branch,PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"Not a power branch");
208       }
209     }
210   }
211 
212   PetscCall(DMRestoreLocalVector(networkdm,&localX));
213 
214   PetscCall(DMLocalToGlobalBegin(networkdm,localF,ADD_VALUES,F));
215   PetscCall(DMLocalToGlobalEnd(networkdm,localF,ADD_VALUES,F));
216   PetscCall(DMRestoreLocalVector(networkdm,&localF));
217 #if 0
218   if (rank == 0) printf("F:\n");
219   PetscCall(VecView(F,PETSC_VIEWER_STDOUT_WORLD));
220 #endif
221   PetscFunctionReturn(0);
222 }
223 
224 PetscErrorCode SetInitialGuess(DM networkdm,Vec X,void* appctx)
225 {
226   PetscInt       nv,ne,i,j,ncomp,offset,key;
227   const PetscInt *vtx,*edges;
228   UserCtx        *user = (UserCtx*)appctx;
229   Vec            localX = user->localXold;
230   UserCtx_Power  appctx_power = (*user).appctx_power;
231   AppCtx_Water   appctx_water = (*user).appctx_water;
232   PetscBool      ghost;
233   PetscScalar    *xarr;
234   VERTEX_Power   bus;
235   VERTEX_Water   vertex;
236   void*          component;
237   GEN            gen;
238 
239   PetscFunctionBegin;
240   PetscCall(VecSet(X,0.0));
241   PetscCall(VecSet(localX,0.0));
242 
243   /* Set initial guess for power subnetwork */
244   PetscCall(DMNetworkGetSubnetwork(networkdm,0,&nv,&ne,&vtx,&edges));
245   PetscCall(SetInitialGuess_Power(networkdm,localX,nv,ne,vtx,edges,&appctx_power));
246 
247   /* Set initial guess for water subnetwork */
248   PetscCall(DMNetworkGetSubnetwork(networkdm,1,&nv,&ne,&vtx,&edges));
249   PetscCall(SetInitialGuess_Water(networkdm,localX,nv,ne,vtx,edges,NULL));
250 
251   /* Set initial guess at the coupling vertex */
252   PetscCall(VecGetArray(localX,&xarr));
253   PetscCall(DMNetworkGetSharedVertices(networkdm,&nv,&vtx));
254   for (i=0; i<nv; i++) {
255     PetscCall(DMNetworkIsGhostVertex(networkdm,vtx[i],&ghost));
256     if (ghost) continue;
257 
258     PetscCall(DMNetworkGetNumComponents(networkdm,vtx[i],&ncomp));
259     for (j=0; j<ncomp; j++) {
260       PetscCall(DMNetworkGetLocalVecOffset(networkdm,vtx[i],j,&offset));
261       PetscCall(DMNetworkGetComponent(networkdm,vtx[i],j,&key,(void**)&component,NULL));
262       if (key == appctx_power.compkey_bus) {
263         bus = (VERTEX_Power)(component);
264         xarr[offset]   = bus->va*PETSC_PI/180.0;
265         xarr[offset+1] = bus->vm;
266       } else if (key == appctx_power.compkey_gen) {
267         gen = (GEN)(component);
268         if (!gen->status) continue;
269         xarr[offset+1] = gen->vs;
270       } else if (key == appctx_water.compkey_vtx) {
271         vertex = (VERTEX_Water)(component);
272         if (vertex->type == VERTEX_TYPE_JUNCTION) {
273           xarr[offset] = 100;
274         } else if (vertex->type == VERTEX_TYPE_RESERVOIR) {
275           xarr[offset] = vertex->res.head;
276         } else {
277           xarr[offset] = vertex->tank.initlvl + vertex->tank.elev;
278         }
279       }
280     }
281   }
282   PetscCall(VecRestoreArray(localX,&xarr));
283 
284   PetscCall(DMLocalToGlobalBegin(networkdm,localX,ADD_VALUES,X));
285   PetscCall(DMLocalToGlobalEnd(networkdm,localX,ADD_VALUES,X));
286   PetscFunctionReturn(0);
287 }
288 
289 int main(int argc,char **argv)
290 {
291   DM               networkdm;
292   PetscLogStage    stage[4];
293   PetscMPIInt      rank,size;
294   PetscInt         Nsubnet=2,numVertices[2],numEdges[2],i,j,nv,ne,it_max=10;
295   const PetscInt   *vtx,*edges;
296   Vec              X,F;
297   SNES             snes,snes_power,snes_water;
298   Mat              Jac;
299   PetscBool        ghost,viewJ=PETSC_FALSE,viewX=PETSC_FALSE,viewDM=PETSC_FALSE,test=PETSC_FALSE,distribute=PETSC_TRUE,flg;
300   UserCtx          user;
301   SNESConvergedReason reason;
302 
303   /* Power subnetwork */
304   UserCtx_Power       *appctx_power  = &user.appctx_power;
305   char                pfdata_file[PETSC_MAX_PATH_LEN] = "power/case9.m";
306   PFDATA              *pfdata = NULL;
307   PetscInt            genj,loadj,*edgelist_power = NULL,power_netnum;
308   PetscScalar         Sbase = 0.0;
309 
310   /* Water subnetwork */
311   AppCtx_Water        *appctx_water = &user.appctx_water;
312   WATERDATA           *waterdata = NULL;
313   char                waterdata_file[PETSC_MAX_PATH_LEN] = "water/sample1.inp";
314   PetscInt            *edgelist_water = NULL,water_netnum;
315 
316   /* Shared vertices between subnetworks */
317   PetscInt           power_svtx,water_svtx;
318 
319   PetscFunctionBeginUser;
320   PetscCall(PetscInitialize(&argc,&argv,"ex1options",help));
321   PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD,&rank));
322   PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD,&size));
323 
324   /* (1) Read Data - Only rank 0 reads the data */
325   /*--------------------------------------------*/
326   PetscCall(PetscLogStageRegister("Read Data",&stage[0]));
327   PetscCall(PetscLogStagePush(stage[0]));
328 
329   for (i=0; i<Nsubnet; i++) {
330     numVertices[i] = 0;
331     numEdges[i]    = 0;
332   }
333 
334   /* All processes READ THE DATA FOR THE FIRST SUBNETWORK: Electric Power Grid */
335   /* Used for shared vertex, because currently the coupling info must be available in all processes!!! */
336   PetscCall(PetscOptionsGetString(NULL,NULL,"-pfdata",pfdata_file,PETSC_MAX_PATH_LEN-1,NULL));
337   PetscCall(PetscNew(&pfdata));
338   PetscCall(PFReadMatPowerData(pfdata,pfdata_file));
339   if (rank == 0) {
340     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));
341   }
342   Sbase = pfdata->sbase;
343   if (rank == 0) { /* proc[0] will create Electric Power Grid */
344     numEdges[0]    = pfdata->nbranch;
345     numVertices[0] = pfdata->nbus;
346 
347     PetscCall(PetscMalloc1(2*numEdges[0],&edgelist_power));
348     PetscCall(GetListofEdges_Power(pfdata,edgelist_power));
349   }
350   /* Broadcast power Sbase to all processors */
351   PetscCallMPI(MPI_Bcast(&Sbase,1,MPIU_SCALAR,0,PETSC_COMM_WORLD));
352   appctx_power->Sbase     = Sbase;
353   appctx_power->jac_error = PETSC_FALSE;
354   /* If external option activated. Introduce error in jacobian */
355   PetscCall(PetscOptionsHasName(NULL,NULL, "-jac_error", &appctx_power->jac_error));
356 
357   /* All processes READ THE DATA FOR THE SECOND SUBNETWORK: Water */
358   /* Used for shared vertex, because currently the coupling info must be available in all processes!!! */
359   PetscCall(PetscNew(&waterdata));
360   PetscCall(PetscOptionsGetString(NULL,NULL,"-waterdata",waterdata_file,PETSC_MAX_PATH_LEN-1,NULL));
361   PetscCall(WaterReadData(waterdata,waterdata_file));
362   if (size == 1 || (size > 1 && rank == 1)) {
363     PetscCall(PetscCalloc1(2*waterdata->nedge,&edgelist_water));
364     PetscCall(GetListofEdges_Water(waterdata,edgelist_water));
365     numEdges[1]    = waterdata->nedge;
366     numVertices[1] = waterdata->nvertex;
367   }
368   PetscLogStagePop();
369 
370   /* (2) Create a network consist of two subnetworks */
371   /*-------------------------------------------------*/
372   PetscCall(PetscLogStageRegister("Net Setup",&stage[1]));
373   PetscCall(PetscLogStagePush(stage[1]));
374 
375   PetscCall(PetscOptionsGetBool(NULL,NULL,"-viewDM",&viewDM,NULL));
376   PetscCall(PetscOptionsGetBool(NULL,NULL,"-test",&test,NULL));
377   PetscCall(PetscOptionsGetBool(NULL,NULL,"-distribute",&distribute,NULL));
378 
379   /* Create an empty network object */
380   PetscCall(DMNetworkCreate(PETSC_COMM_WORLD,&networkdm));
381 
382   /* Register the components in the network */
383   PetscCall(DMNetworkRegisterComponent(networkdm,"branchstruct",sizeof(struct _p_EDGE_Power),&appctx_power->compkey_branch));
384   PetscCall(DMNetworkRegisterComponent(networkdm,"busstruct",sizeof(struct _p_VERTEX_Power),&appctx_power->compkey_bus));
385   PetscCall(DMNetworkRegisterComponent(networkdm,"genstruct",sizeof(struct _p_GEN),&appctx_power->compkey_gen));
386   PetscCall(DMNetworkRegisterComponent(networkdm,"loadstruct",sizeof(struct _p_LOAD),&appctx_power->compkey_load));
387 
388   PetscCall(DMNetworkRegisterComponent(networkdm,"edge_water",sizeof(struct _p_EDGE_Water),&appctx_water->compkey_edge));
389   PetscCall(DMNetworkRegisterComponent(networkdm,"vertex_water",sizeof(struct _p_VERTEX_Water),&appctx_water->compkey_vtx));
390 #if 0
391   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"power->compkey_branch %d\n",appctx_power->compkey_branch));
392   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"power->compkey_bus    %d\n",appctx_power->compkey_bus));
393   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"power->compkey_gen    %d\n",appctx_power->compkey_gen));
394   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"power->compkey_load   %d\n",appctx_power->compkey_load));
395   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"water->compkey_edge   %d\n",appctx_water->compkey_edge));
396   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"water->compkey_vtx    %d\n",appctx_water->compkey_vtx));
397 #endif
398   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]));
399   PetscCall(PetscSynchronizedFlush(PETSC_COMM_WORLD,PETSC_STDOUT));
400 
401   PetscCall(DMNetworkSetNumSubNetworks(networkdm,PETSC_DECIDE,Nsubnet));
402   PetscCall(DMNetworkAddSubnetwork(networkdm,"power",numEdges[0],edgelist_power,&power_netnum));
403   PetscCall(DMNetworkAddSubnetwork(networkdm,"water",numEdges[1],edgelist_water,&water_netnum));
404 
405   /* vertex subnet[0].4 shares with vertex subnet[1].0 */
406   power_svtx = 4; water_svtx = 0;
407   PetscCall(DMNetworkAddSharedVertices(networkdm,power_netnum,water_netnum,1,&power_svtx,&water_svtx));
408 
409   /* Set up the network layout */
410   PetscCall(DMNetworkLayoutSetUp(networkdm));
411 
412   /* ADD VARIABLES AND COMPONENTS FOR THE POWER SUBNETWORK */
413   /*-------------------------------------------------------*/
414   genj = 0; loadj = 0;
415   PetscCall(DMNetworkGetSubnetwork(networkdm,power_netnum,&nv,&ne,&vtx,&edges));
416 
417   for (i = 0; i < ne; i++) {
418     PetscCall(DMNetworkAddComponent(networkdm,edges[i],appctx_power->compkey_branch,&pfdata->branch[i],0));
419   }
420 
421   for (i = 0; i < nv; i++) {
422     PetscCall(DMNetworkIsSharedVertex(networkdm,vtx[i],&flg));
423     if (flg) continue;
424 
425     PetscCall(DMNetworkAddComponent(networkdm,vtx[i],appctx_power->compkey_bus,&pfdata->bus[i],2));
426     if (pfdata->bus[i].ngen) {
427       for (j = 0; j < pfdata->bus[i].ngen; j++) {
428         PetscCall(DMNetworkAddComponent(networkdm,vtx[i],appctx_power->compkey_gen,&pfdata->gen[genj++],0));
429       }
430     }
431     if (pfdata->bus[i].nload) {
432       for (j=0; j < pfdata->bus[i].nload; j++) {
433         PetscCall(DMNetworkAddComponent(networkdm,vtx[i],appctx_power->compkey_load,&pfdata->load[loadj++],0));
434       }
435     }
436   }
437 
438   /* ADD VARIABLES AND COMPONENTS FOR THE WATER SUBNETWORK */
439   /*-------------------------------------------------------*/
440   PetscCall(DMNetworkGetSubnetwork(networkdm,water_netnum,&nv,&ne,&vtx,&edges));
441   for (i = 0; i < ne; i++) {
442     PetscCall(DMNetworkAddComponent(networkdm,edges[i],appctx_water->compkey_edge,&waterdata->edge[i],0));
443   }
444 
445   for (i = 0; i < nv; i++) {
446     PetscCall(DMNetworkIsSharedVertex(networkdm,vtx[i],&flg));
447     if (flg) continue;
448 
449     PetscCall(DMNetworkAddComponent(networkdm,vtx[i],appctx_water->compkey_vtx,&waterdata->vertex[i],1));
450   }
451 
452   /* 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 */
453   /*----------------------------------------------------------------------------------------------------------------------------*/
454   PetscCall(DMNetworkGetSharedVertices(networkdm,&nv,&vtx));
455   for (i = 0; i < nv; i++) {
456     /* power */
457     PetscCall(DMNetworkAddComponent(networkdm,vtx[i],appctx_power->compkey_bus,&pfdata->bus[4],2));
458     /* bus[4] is a load, add its component */
459     PetscCall(DMNetworkAddComponent(networkdm,vtx[i],appctx_power->compkey_load,&pfdata->load[0],0));
460 
461     /* water */
462     PetscCall(DMNetworkAddComponent(networkdm,vtx[i],appctx_water->compkey_vtx,&waterdata->vertex[0],1));
463   }
464 
465   /* Set up DM for use */
466   PetscCall(DMSetUp(networkdm));
467   if (viewDM) {
468     PetscCall(PetscPrintf(PETSC_COMM_WORLD,"\nAfter DMSetUp, DMView:\n"));
469     PetscCall(DMView(networkdm,PETSC_VIEWER_STDOUT_WORLD));
470   }
471 
472   /* Free user objects */
473   PetscCall(PetscFree(edgelist_power));
474   PetscCall(PetscFree(pfdata->bus));
475   PetscCall(PetscFree(pfdata->gen));
476   PetscCall(PetscFree(pfdata->branch));
477   PetscCall(PetscFree(pfdata->load));
478   PetscCall(PetscFree(pfdata));
479 
480   PetscCall(PetscFree(edgelist_water));
481   PetscCall(PetscFree(waterdata->vertex));
482   PetscCall(PetscFree(waterdata->edge));
483   PetscCall(PetscFree(waterdata));
484 
485   /* Re-distribute networkdm to multiple processes for better job balance */
486   if (size >1 && distribute) {
487     PetscCall(DMNetworkDistribute(&networkdm,0));
488     if (viewDM) {
489       PetscCall(PetscPrintf(PETSC_COMM_WORLD,"\nAfter DMNetworkDistribute, DMView:\n"));
490       PetscCall(DMView(networkdm,PETSC_VIEWER_STDOUT_WORLD));
491     }
492   }
493 
494   /* Test DMNetworkGetSubnetwork() and DMNetworkGetSubnetworkSharedVertices() */
495   if (test) {
496     PetscInt  v,gidx;
497     PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
498     for (i=0; i<Nsubnet; i++) {
499       PetscCall(DMNetworkGetSubnetwork(networkdm,i,&nv,&ne,&vtx,&edges));
500       PetscCall(PetscPrintf(PETSC_COMM_SELF,"[%d] After distribute, subnet[%" PetscInt_FMT "] ne %" PetscInt_FMT ", nv %" PetscInt_FMT "\n",rank,i,ne,nv));
501       PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
502 
503       for (v=0; v<nv; v++) {
504         PetscCall(DMNetworkIsGhostVertex(networkdm,vtx[v],&ghost));
505         PetscCall(DMNetworkGetGlobalVertexIndex(networkdm,vtx[v],&gidx));
506         PetscCall(PetscPrintf(PETSC_COMM_SELF,"[%d] subnet[%" PetscInt_FMT "] v %" PetscInt_FMT " %" PetscInt_FMT "; ghost %d\n",rank,i,vtx[v],gidx,ghost));
507       }
508       PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
509     }
510     PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
511 
512     PetscCall(DMNetworkGetSharedVertices(networkdm,&nv,&vtx));
513     PetscCall(PetscPrintf(PETSC_COMM_SELF,"[%d] After distribute, num of shared vertices nsv = %" PetscInt_FMT "\n",rank,nv));
514     for (v=0; v<nv; v++) {
515       PetscCall(DMNetworkGetGlobalVertexIndex(networkdm,vtx[v],&gidx));
516       PetscCall(PetscPrintf(PETSC_COMM_SELF,"[%d] sv %" PetscInt_FMT ", gidx=%" PetscInt_FMT "\n",rank,vtx[v],gidx));
517     }
518     PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
519   }
520 
521   /* Create solution vector X */
522   PetscCall(DMCreateGlobalVector(networkdm,&X));
523   PetscCall(VecDuplicate(X,&F));
524   PetscCall(DMGetLocalVector(networkdm,&user.localXold));
525   PetscLogStagePop();
526 
527   /* (3) Setup Solvers */
528   /*-------------------*/
529   PetscCall(PetscOptionsGetBool(NULL,NULL,"-viewJ",&viewJ,NULL));
530   PetscCall(PetscOptionsGetBool(NULL,NULL,"-viewX",&viewX,NULL));
531 
532   PetscCall(PetscLogStageRegister("SNES Setup",&stage[2]));
533   PetscCall(PetscLogStagePush(stage[2]));
534 
535   PetscCall(SetInitialGuess(networkdm,X,&user));
536 
537   /* Create coupled snes */
538   /*-------------------- */
539   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"SNES_coupled setup ......\n"));
540   user.subsnes_id = Nsubnet;
541   PetscCall(SNESCreate(PETSC_COMM_WORLD,&snes));
542   PetscCall(SNESSetDM(snes,networkdm));
543   PetscCall(SNESSetOptionsPrefix(snes,"coupled_"));
544   PetscCall(SNESSetFunction(snes,F,FormFunction,&user));
545   PetscCall(SNESMonitorSet(snes,UserMonitor,&user,NULL));
546   PetscCall(SNESSetFromOptions(snes));
547 
548   if (viewJ) {
549     /* View Jac structure */
550     PetscCall(SNESGetJacobian(snes,&Jac,NULL,NULL,NULL));
551     PetscCall(MatView(Jac,PETSC_VIEWER_DRAW_WORLD));
552   }
553 
554   if (viewX) {
555     PetscCall(PetscPrintf(PETSC_COMM_WORLD,"Solution:\n"));
556     PetscCall(VecView(X,PETSC_VIEWER_STDOUT_WORLD));
557   }
558 
559   if (viewJ) {
560     /* View assembled Jac */
561     PetscCall(MatView(Jac,PETSC_VIEWER_DRAW_WORLD));
562   }
563 
564   /* Create snes_power */
565   /*-------------------*/
566   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"SNES_power setup ......\n"));
567 
568   user.subsnes_id = 0;
569   PetscCall(SNESCreate(PETSC_COMM_WORLD,&snes_power));
570   PetscCall(SNESSetDM(snes_power,networkdm));
571   PetscCall(SNESSetOptionsPrefix(snes_power,"power_"));
572   PetscCall(SNESSetFunction(snes_power,F,FormFunction,&user));
573   PetscCall(SNESMonitorSet(snes_power,UserMonitor,&user,NULL));
574 
575   /* Use user-provide Jacobian */
576   PetscCall(DMCreateMatrix(networkdm,&Jac));
577   PetscCall(SNESSetJacobian(snes_power,Jac,Jac,FormJacobian_subPower,&user));
578   PetscCall(SNESSetFromOptions(snes_power));
579 
580   if (viewX) {
581     PetscCall(PetscPrintf(PETSC_COMM_WORLD,"Power Solution:\n"));
582     PetscCall(VecView(X,PETSC_VIEWER_STDOUT_WORLD));
583   }
584   if (viewJ) {
585     PetscCall(PetscPrintf(PETSC_COMM_WORLD,"Power Jac:\n"));
586     PetscCall(SNESGetJacobian(snes_power,&Jac,NULL,NULL,NULL));
587     PetscCall(MatView(Jac,PETSC_VIEWER_DRAW_WORLD));
588     /* PetscCall(MatView(Jac,PETSC_VIEWER_STDOUT_WORLD)); */
589   }
590 
591   /* Create snes_water */
592   /*-------------------*/
593   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"SNES_water setup......\n"));
594 
595   user.subsnes_id = 1;
596   PetscCall(SNESCreate(PETSC_COMM_WORLD,&snes_water));
597   PetscCall(SNESSetDM(snes_water,networkdm));
598   PetscCall(SNESSetOptionsPrefix(snes_water,"water_"));
599   PetscCall(SNESSetFunction(snes_water,F,FormFunction,&user));
600   PetscCall(SNESMonitorSet(snes_water,UserMonitor,&user,NULL));
601   PetscCall(SNESSetFromOptions(snes_water));
602 
603   if (viewX) {
604     PetscCall(PetscPrintf(PETSC_COMM_WORLD,"Water Solution:\n"));
605     PetscCall(VecView(X,PETSC_VIEWER_STDOUT_WORLD));
606   }
607   PetscCall(PetscLogStagePop());
608 
609   /* (4) Solve */
610   /*-----------*/
611   PetscCall(PetscLogStageRegister("SNES Solve",&stage[3]));
612   PetscCall(PetscLogStagePush(stage[3]));
613   user.it = 0;
614   reason  = SNES_DIVERGED_DTOL;
615   while (user.it < it_max && (PetscInt)reason<0) {
616 #if 0
617     user.subsnes_id = 0;
618     PetscCall(SNESSolve(snes_power,NULL,X));
619 
620     user.subsnes_id = 1;
621     PetscCall(SNESSolve(snes_water,NULL,X));
622 #endif
623     user.subsnes_id = Nsubnet;
624     PetscCall(SNESSolve(snes,NULL,X));
625 
626     PetscCall(SNESGetConvergedReason(snes,&reason));
627     user.it++;
628   }
629   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"Coupled_SNES converged in %" PetscInt_FMT " iterations\n",user.it));
630   if (viewX) {
631     PetscCall(PetscPrintf(PETSC_COMM_WORLD,"Final Solution:\n"));
632     PetscCall(VecView(X,PETSC_VIEWER_STDOUT_WORLD));
633   }
634   PetscCall(PetscLogStagePop());
635 
636   /* Free objects */
637   /* -------------*/
638   PetscCall(VecDestroy(&X));
639   PetscCall(VecDestroy(&F));
640   PetscCall(DMRestoreLocalVector(networkdm,&user.localXold));
641 
642   PetscCall(SNESDestroy(&snes));
643   PetscCall(MatDestroy(&Jac));
644   PetscCall(SNESDestroy(&snes_power));
645   PetscCall(SNESDestroy(&snes_water));
646 
647   PetscCall(DMDestroy(&networkdm));
648   PetscCall(PetscFinalize());
649   return 0;
650 }
651 
652 /*TEST
653 
654    build:
655      requires: !complex double defined(PETSC_HAVE_ATTRIBUTEALIGNED)
656      depends: power/PFReadData.c power/pffunctions.c water/waterreaddata.c water/waterfunctions.c
657 
658    test:
659       args: -coupled_snes_converged_reason -options_left no -viewDM
660       localrunfiles: ex1options power/case9.m water/sample1.inp
661       output_file: output/ex1.out
662 
663    test:
664       suffix: 2
665       nsize: 3
666       args: -coupled_snes_converged_reason -options_left no -petscpartitioner_type parmetis
667       localrunfiles: ex1options power/case9.m water/sample1.inp
668       output_file: output/ex1_2.out
669       requires: parmetis
670 
671 #   test:
672 #      suffix: 3
673 #      nsize: 3
674 #      args: -coupled_snes_converged_reason -options_left no -distribute false
675 #      localrunfiles: ex1options power/case9.m water/sample1.inp
676 #      output_file: output/ex1_2.out
677 
678    test:
679       suffix: 4
680       nsize: 4
681       args: -coupled_snes_converged_reason -options_left no -petscpartitioner_type simple -viewDM
682       localrunfiles: ex1options power/case9.m water/sample1.inp
683       output_file: output/ex1_4.out
684 
685 TEST*/
686