xref: /petsc/src/snes/tutorials/network/ex1.c (revision 11486bccf1aeea1ca5536228f99d437b39bdaca6)
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) { /* water_compkey_edge */
207         EDGE_Water        edge=(EDGE_Water)component;
208         if (edge->type == EDGE_TYPE_PUMP) {
209           /* printf("  connected edge[%" PetscInt_FMT "]=%" PetscInt_FMT " has keye=%" PetscInt_FMT ", is appctx_water.compkey_edge with EDGE_TYPE_PUMP\n",k,e,keye); */
210         }
211       } else { /* ower->compkey_branch */
212         PetscCheck(keye == appctx_power.compkey_branch,PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"Not a power branch");
213       }
214     }
215   }
216 
217   PetscCall(DMRestoreLocalVector(networkdm,&localX));
218 
219   PetscCall(DMLocalToGlobalBegin(networkdm,localF,ADD_VALUES,F));
220   PetscCall(DMLocalToGlobalEnd(networkdm,localF,ADD_VALUES,F));
221   PetscCall(DMRestoreLocalVector(networkdm,&localF));
222 #if 0
223   if (rank == 0) printf("F:\n");
224   PetscCall(VecView(F,PETSC_VIEWER_STDOUT_WORLD));
225 #endif
226   PetscFunctionReturn(0);
227 }
228 
229 PetscErrorCode SetInitialGuess(DM networkdm,Vec X,void* appctx)
230 {
231   PetscInt       nv,ne,i,j,ncomp,offset,key;
232   const PetscInt *vtx,*edges;
233   UserCtx        *user = (UserCtx*)appctx;
234   Vec            localX = user->localXold;
235   UserCtx_Power  appctx_power = (*user).appctx_power;
236   AppCtx_Water   appctx_water = (*user).appctx_water;
237   PetscBool      ghost;
238   PetscScalar    *xarr;
239   VERTEX_Power   bus;
240   VERTEX_Water   vertex;
241   void*          component;
242   GEN            gen;
243 
244   PetscFunctionBegin;
245   PetscCall(VecSet(X,0.0));
246   PetscCall(VecSet(localX,0.0));
247 
248   /* Set initial guess for power subnetwork */
249   PetscCall(DMNetworkGetSubnetwork(networkdm,0,&nv,&ne,&vtx,&edges));
250   PetscCall(SetInitialGuess_Power(networkdm,localX,nv,ne,vtx,edges,&appctx_power));
251 
252   /* Set initial guess for water subnetwork */
253   PetscCall(DMNetworkGetSubnetwork(networkdm,1,&nv,&ne,&vtx,&edges));
254   PetscCall(SetInitialGuess_Water(networkdm,localX,nv,ne,vtx,edges,NULL));
255 
256   /* Set initial guess at the coupling vertex */
257   PetscCall(VecGetArray(localX,&xarr));
258   PetscCall(DMNetworkGetSharedVertices(networkdm,&nv,&vtx));
259   for (i=0; i<nv; i++) {
260     PetscCall(DMNetworkIsGhostVertex(networkdm,vtx[i],&ghost));
261     if (ghost) continue;
262 
263     PetscCall(DMNetworkGetNumComponents(networkdm,vtx[i],&ncomp));
264     for (j=0; j<ncomp; j++) {
265       PetscCall(DMNetworkGetLocalVecOffset(networkdm,vtx[i],j,&offset));
266       PetscCall(DMNetworkGetComponent(networkdm,vtx[i],j,&key,(void**)&component,NULL));
267       if (key == appctx_power.compkey_bus) {
268         bus = (VERTEX_Power)(component);
269         xarr[offset]   = bus->va*PETSC_PI/180.0;
270         xarr[offset+1] = bus->vm;
271       } else if (key == appctx_power.compkey_gen) {
272         gen = (GEN)(component);
273         if (!gen->status) continue;
274         xarr[offset+1] = gen->vs;
275       } else if (key == appctx_water.compkey_vtx) {
276         vertex = (VERTEX_Water)(component);
277         if (vertex->type == VERTEX_TYPE_JUNCTION) {
278           xarr[offset] = 100;
279         } else if (vertex->type == VERTEX_TYPE_RESERVOIR) {
280           xarr[offset] = vertex->res.head;
281         } else {
282           xarr[offset] = vertex->tank.initlvl + vertex->tank.elev;
283         }
284       }
285     }
286   }
287   PetscCall(VecRestoreArray(localX,&xarr));
288 
289   PetscCall(DMLocalToGlobalBegin(networkdm,localX,ADD_VALUES,X));
290   PetscCall(DMLocalToGlobalEnd(networkdm,localX,ADD_VALUES,X));
291   PetscFunctionReturn(0);
292 }
293 
294 int main(int argc,char **argv)
295 {
296   DM               networkdm;
297   PetscLogStage    stage[4];
298   PetscMPIInt      rank,size;
299   PetscInt         Nsubnet=2,numVertices[2],numEdges[2],i,j,nv,ne,it_max=10;
300   const PetscInt   *vtx,*edges;
301   Vec              X,F;
302   SNES             snes,snes_power,snes_water;
303   Mat              Jac;
304   PetscBool        ghost,viewJ=PETSC_FALSE,viewX=PETSC_FALSE,viewDM=PETSC_FALSE,test=PETSC_FALSE,distribute=PETSC_TRUE,flg;
305   UserCtx          user;
306   SNESConvergedReason reason;
307 
308   /* Power subnetwork */
309   UserCtx_Power       *appctx_power  = &user.appctx_power;
310   char                pfdata_file[PETSC_MAX_PATH_LEN] = "power/case9.m";
311   PFDATA              *pfdata = NULL;
312   PetscInt            genj,loadj,*edgelist_power = NULL,power_netnum;
313   PetscScalar         Sbase = 0.0;
314 
315   /* Water subnetwork */
316   AppCtx_Water        *appctx_water = &user.appctx_water;
317   WATERDATA           *waterdata = NULL;
318   char                waterdata_file[PETSC_MAX_PATH_LEN] = "water/sample1.inp";
319   PetscInt            *edgelist_water = NULL,water_netnum;
320 
321   /* Shared vertices between subnetworks */
322   PetscInt           power_svtx,water_svtx;
323 
324   PetscFunctionBeginUser;
325   PetscCall(PetscInitialize(&argc,&argv,"ex1options",help));
326   PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD,&rank));
327   PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD,&size));
328 
329   /* (1) Read Data - Only rank 0 reads the data */
330   /*--------------------------------------------*/
331   PetscCall(PetscLogStageRegister("Read Data",&stage[0]));
332   PetscCall(PetscLogStagePush(stage[0]));
333 
334   for (i=0; i<Nsubnet; i++) {
335     numVertices[i] = 0;
336     numEdges[i]    = 0;
337   }
338 
339   /* All processes READ THE DATA FOR THE FIRST SUBNETWORK: Electric Power Grid */
340   /* Used for shared vertex, because currently the coupling info must be available in all processes!!! */
341   PetscCall(PetscOptionsGetString(NULL,NULL,"-pfdata",pfdata_file,PETSC_MAX_PATH_LEN-1,NULL));
342   PetscCall(PetscNew(&pfdata));
343   PetscCall(PFReadMatPowerData(pfdata,pfdata_file));
344   if (rank == 0) {
345     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));
346   }
347   Sbase = pfdata->sbase;
348   if (rank == 0) { /* proc[0] will create Electric Power Grid */
349     numEdges[0]    = pfdata->nbranch;
350     numVertices[0] = pfdata->nbus;
351 
352     PetscCall(PetscMalloc1(2*numEdges[0],&edgelist_power));
353     PetscCall(GetListofEdges_Power(pfdata,edgelist_power));
354   }
355   /* Broadcast power Sbase to all processors */
356   PetscCallMPI(MPI_Bcast(&Sbase,1,MPIU_SCALAR,0,PETSC_COMM_WORLD));
357   appctx_power->Sbase     = Sbase;
358   appctx_power->jac_error = PETSC_FALSE;
359   /* If external option activated. Introduce error in jacobian */
360   PetscCall(PetscOptionsHasName(NULL,NULL, "-jac_error", &appctx_power->jac_error));
361 
362   /* All processes READ THE DATA FOR THE SECOND SUBNETWORK: Water */
363   /* Used for shared vertex, because currently the coupling info must be available in all processes!!! */
364   PetscCall(PetscNew(&waterdata));
365   PetscCall(PetscOptionsGetString(NULL,NULL,"-waterdata",waterdata_file,PETSC_MAX_PATH_LEN-1,NULL));
366   PetscCall(WaterReadData(waterdata,waterdata_file));
367   if (size == 1 || (size > 1 && rank == 1)) {
368     PetscCall(PetscCalloc1(2*waterdata->nedge,&edgelist_water));
369     PetscCall(GetListofEdges_Water(waterdata,edgelist_water));
370     numEdges[1]    = waterdata->nedge;
371     numVertices[1] = waterdata->nvertex;
372   }
373   PetscLogStagePop();
374 
375   /* (2) Create a network consist of two subnetworks */
376   /*-------------------------------------------------*/
377   PetscCall(PetscLogStageRegister("Net Setup",&stage[1]));
378   PetscCall(PetscLogStagePush(stage[1]));
379 
380   PetscCall(PetscOptionsGetBool(NULL,NULL,"-viewDM",&viewDM,NULL));
381   PetscCall(PetscOptionsGetBool(NULL,NULL,"-test",&test,NULL));
382   PetscCall(PetscOptionsGetBool(NULL,NULL,"-distribute",&distribute,NULL));
383 
384   /* Create an empty network object */
385   PetscCall(DMNetworkCreate(PETSC_COMM_WORLD,&networkdm));
386 
387   /* Register the components in the network */
388   PetscCall(DMNetworkRegisterComponent(networkdm,"branchstruct",sizeof(struct _p_EDGE_Power),&appctx_power->compkey_branch));
389   PetscCall(DMNetworkRegisterComponent(networkdm,"busstruct",sizeof(struct _p_VERTEX_Power),&appctx_power->compkey_bus));
390   PetscCall(DMNetworkRegisterComponent(networkdm,"genstruct",sizeof(struct _p_GEN),&appctx_power->compkey_gen));
391   PetscCall(DMNetworkRegisterComponent(networkdm,"loadstruct",sizeof(struct _p_LOAD),&appctx_power->compkey_load));
392 
393   PetscCall(DMNetworkRegisterComponent(networkdm,"edge_water",sizeof(struct _p_EDGE_Water),&appctx_water->compkey_edge));
394   PetscCall(DMNetworkRegisterComponent(networkdm,"vertex_water",sizeof(struct _p_VERTEX_Water),&appctx_water->compkey_vtx));
395 #if 0
396   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"power->compkey_branch %d\n",appctx_power->compkey_branch));
397   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"power->compkey_bus    %d\n",appctx_power->compkey_bus));
398   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"power->compkey_gen    %d\n",appctx_power->compkey_gen));
399   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"power->compkey_load   %d\n",appctx_power->compkey_load));
400   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"water->compkey_edge   %d\n",appctx_water->compkey_edge));
401   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"water->compkey_vtx    %d\n",appctx_water->compkey_vtx));
402 #endif
403   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]));
404   PetscCall(PetscSynchronizedFlush(PETSC_COMM_WORLD,PETSC_STDOUT));
405 
406   PetscCall(DMNetworkSetNumSubNetworks(networkdm,PETSC_DECIDE,Nsubnet));
407   PetscCall(DMNetworkAddSubnetwork(networkdm,"power",numEdges[0],edgelist_power,&power_netnum));
408   PetscCall(DMNetworkAddSubnetwork(networkdm,"water",numEdges[1],edgelist_water,&water_netnum));
409 
410   /* vertex subnet[0].4 shares with vertex subnet[1].0 */
411   power_svtx = 4; water_svtx = 0;
412   PetscCall(DMNetworkAddSharedVertices(networkdm,power_netnum,water_netnum,1,&power_svtx,&water_svtx));
413 
414   /* Set up the network layout */
415   PetscCall(DMNetworkLayoutSetUp(networkdm));
416 
417   /* ADD VARIABLES AND COMPONENTS FOR THE POWER SUBNETWORK */
418   /*-------------------------------------------------------*/
419   genj = 0; loadj = 0;
420   PetscCall(DMNetworkGetSubnetwork(networkdm,power_netnum,&nv,&ne,&vtx,&edges));
421 
422   for (i = 0; i < ne; i++) {
423     PetscCall(DMNetworkAddComponent(networkdm,edges[i],appctx_power->compkey_branch,&pfdata->branch[i],0));
424   }
425 
426   for (i = 0; i < nv; i++) {
427     PetscCall(DMNetworkIsSharedVertex(networkdm,vtx[i],&flg));
428     if (flg) continue;
429 
430     PetscCall(DMNetworkAddComponent(networkdm,vtx[i],appctx_power->compkey_bus,&pfdata->bus[i],2));
431     if (pfdata->bus[i].ngen) {
432       for (j = 0; j < pfdata->bus[i].ngen; j++) {
433         PetscCall(DMNetworkAddComponent(networkdm,vtx[i],appctx_power->compkey_gen,&pfdata->gen[genj++],0));
434       }
435     }
436     if (pfdata->bus[i].nload) {
437       for (j=0; j < pfdata->bus[i].nload; j++) {
438         PetscCall(DMNetworkAddComponent(networkdm,vtx[i],appctx_power->compkey_load,&pfdata->load[loadj++],0));
439       }
440     }
441   }
442 
443   /* ADD VARIABLES AND COMPONENTS FOR THE WATER SUBNETWORK */
444   /*-------------------------------------------------------*/
445   PetscCall(DMNetworkGetSubnetwork(networkdm,water_netnum,&nv,&ne,&vtx,&edges));
446   for (i = 0; i < ne; i++) {
447     PetscCall(DMNetworkAddComponent(networkdm,edges[i],appctx_water->compkey_edge,&waterdata->edge[i],0));
448   }
449 
450   for (i = 0; i < nv; i++) {
451     PetscCall(DMNetworkIsSharedVertex(networkdm,vtx[i],&flg));
452     if (flg) continue;
453 
454     PetscCall(DMNetworkAddComponent(networkdm,vtx[i],appctx_water->compkey_vtx,&waterdata->vertex[i],1));
455   }
456 
457   /* 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 */
458   /*----------------------------------------------------------------------------------------------------------------------------*/
459   PetscCall(DMNetworkGetSharedVertices(networkdm,&nv,&vtx));
460   for (i = 0; i < nv; i++) {
461     /* power */
462     PetscCall(DMNetworkAddComponent(networkdm,vtx[i],appctx_power->compkey_bus,&pfdata->bus[4],2));
463     /* bus[4] is a load, add its component */
464     PetscCall(DMNetworkAddComponent(networkdm,vtx[i],appctx_power->compkey_load,&pfdata->load[0],0));
465 
466     /* water */
467     PetscCall(DMNetworkAddComponent(networkdm,vtx[i],appctx_water->compkey_vtx,&waterdata->vertex[0],1));
468   }
469 
470   /* Set up DM for use */
471   PetscCall(DMSetUp(networkdm));
472   if (viewDM) {
473     PetscCall(PetscPrintf(PETSC_COMM_WORLD,"\nAfter DMSetUp, DMView:\n"));
474     PetscCall(DMView(networkdm,PETSC_VIEWER_STDOUT_WORLD));
475   }
476 
477   /* Free user objects */
478   PetscCall(PetscFree(edgelist_power));
479   PetscCall(PetscFree(pfdata->bus));
480   PetscCall(PetscFree(pfdata->gen));
481   PetscCall(PetscFree(pfdata->branch));
482   PetscCall(PetscFree(pfdata->load));
483   PetscCall(PetscFree(pfdata));
484 
485   PetscCall(PetscFree(edgelist_water));
486   PetscCall(PetscFree(waterdata->vertex));
487   PetscCall(PetscFree(waterdata->edge));
488   PetscCall(PetscFree(waterdata));
489 
490   /* Re-distribute networkdm to multiple processes for better job balance */
491   if (size >1 && distribute) {
492     PetscCall(DMNetworkDistribute(&networkdm,0));
493     if (viewDM) {
494       PetscCall(PetscPrintf(PETSC_COMM_WORLD,"\nAfter DMNetworkDistribute, DMView:\n"));
495       PetscCall(DMView(networkdm,PETSC_VIEWER_STDOUT_WORLD));
496     }
497   }
498 
499   /* Test DMNetworkGetSubnetwork() and DMNetworkGetSubnetworkSharedVertices() */
500   if (test) {
501     PetscInt  v,gidx;
502     PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
503     for (i=0; i<Nsubnet; i++) {
504       PetscCall(DMNetworkGetSubnetwork(networkdm,i,&nv,&ne,&vtx,&edges));
505       PetscCall(PetscPrintf(PETSC_COMM_SELF,"[%d] After distribute, subnet[%" PetscInt_FMT "] ne %" PetscInt_FMT ", nv %" PetscInt_FMT "\n",rank,i,ne,nv));
506       PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
507 
508       for (v=0; v<nv; v++) {
509         PetscCall(DMNetworkIsGhostVertex(networkdm,vtx[v],&ghost));
510         PetscCall(DMNetworkGetGlobalVertexIndex(networkdm,vtx[v],&gidx));
511         PetscCall(PetscPrintf(PETSC_COMM_SELF,"[%d] subnet[%" PetscInt_FMT "] v %" PetscInt_FMT " %" PetscInt_FMT "; ghost %d\n",rank,i,vtx[v],gidx,ghost));
512       }
513       PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
514     }
515     PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
516 
517     PetscCall(DMNetworkGetSharedVertices(networkdm,&nv,&vtx));
518     PetscCall(PetscPrintf(PETSC_COMM_SELF,"[%d] After distribute, num of shared vertices nsv = %" PetscInt_FMT "\n",rank,nv));
519     for (v=0; v<nv; v++) {
520       PetscCall(DMNetworkGetGlobalVertexIndex(networkdm,vtx[v],&gidx));
521       PetscCall(PetscPrintf(PETSC_COMM_SELF,"[%d] sv %" PetscInt_FMT ", gidx=%" PetscInt_FMT "\n",rank,vtx[v],gidx));
522     }
523     PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
524   }
525 
526   /* Create solution vector X */
527   PetscCall(DMCreateGlobalVector(networkdm,&X));
528   PetscCall(VecDuplicate(X,&F));
529   PetscCall(DMGetLocalVector(networkdm,&user.localXold));
530   PetscLogStagePop();
531 
532   /* (3) Setup Solvers */
533   /*-------------------*/
534   PetscCall(PetscOptionsGetBool(NULL,NULL,"-viewJ",&viewJ,NULL));
535   PetscCall(PetscOptionsGetBool(NULL,NULL,"-viewX",&viewX,NULL));
536 
537   PetscCall(PetscLogStageRegister("SNES Setup",&stage[2]));
538   PetscCall(PetscLogStagePush(stage[2]));
539 
540   PetscCall(SetInitialGuess(networkdm,X,&user));
541 
542   /* Create coupled snes */
543   /*-------------------- */
544   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"SNES_coupled setup ......\n"));
545   user.subsnes_id = Nsubnet;
546   PetscCall(SNESCreate(PETSC_COMM_WORLD,&snes));
547   PetscCall(SNESSetDM(snes,networkdm));
548   PetscCall(SNESSetOptionsPrefix(snes,"coupled_"));
549   PetscCall(SNESSetFunction(snes,F,FormFunction,&user));
550   PetscCall(SNESMonitorSet(snes,UserMonitor,&user,NULL));
551   PetscCall(SNESSetFromOptions(snes));
552 
553   if (viewJ) {
554     /* View Jac structure */
555     PetscCall(SNESGetJacobian(snes,&Jac,NULL,NULL,NULL));
556     PetscCall(MatView(Jac,PETSC_VIEWER_DRAW_WORLD));
557   }
558 
559   if (viewX) {
560     PetscCall(PetscPrintf(PETSC_COMM_WORLD,"Solution:\n"));
561     PetscCall(VecView(X,PETSC_VIEWER_STDOUT_WORLD));
562   }
563 
564   if (viewJ) {
565     /* View assembled Jac */
566     PetscCall(MatView(Jac,PETSC_VIEWER_DRAW_WORLD));
567   }
568 
569   /* Create snes_power */
570   /*-------------------*/
571   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"SNES_power setup ......\n"));
572 
573   user.subsnes_id = 0;
574   PetscCall(SNESCreate(PETSC_COMM_WORLD,&snes_power));
575   PetscCall(SNESSetDM(snes_power,networkdm));
576   PetscCall(SNESSetOptionsPrefix(snes_power,"power_"));
577   PetscCall(SNESSetFunction(snes_power,F,FormFunction,&user));
578   PetscCall(SNESMonitorSet(snes_power,UserMonitor,&user,NULL));
579 
580   /* Use user-provide Jacobian */
581   PetscCall(DMCreateMatrix(networkdm,&Jac));
582   PetscCall(SNESSetJacobian(snes_power,Jac,Jac,FormJacobian_subPower,&user));
583   PetscCall(SNESSetFromOptions(snes_power));
584 
585   if (viewX) {
586     PetscCall(PetscPrintf(PETSC_COMM_WORLD,"Power Solution:\n"));
587     PetscCall(VecView(X,PETSC_VIEWER_STDOUT_WORLD));
588   }
589   if (viewJ) {
590     PetscCall(PetscPrintf(PETSC_COMM_WORLD,"Power Jac:\n"));
591     PetscCall(SNESGetJacobian(snes_power,&Jac,NULL,NULL,NULL));
592     PetscCall(MatView(Jac,PETSC_VIEWER_DRAW_WORLD));
593     /* PetscCall(MatView(Jac,PETSC_VIEWER_STDOUT_WORLD)); */
594   }
595 
596   /* Create snes_water */
597   /*-------------------*/
598   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"SNES_water setup......\n"));
599 
600   user.subsnes_id = 1;
601   PetscCall(SNESCreate(PETSC_COMM_WORLD,&snes_water));
602   PetscCall(SNESSetDM(snes_water,networkdm));
603   PetscCall(SNESSetOptionsPrefix(snes_water,"water_"));
604   PetscCall(SNESSetFunction(snes_water,F,FormFunction,&user));
605   PetscCall(SNESMonitorSet(snes_water,UserMonitor,&user,NULL));
606   PetscCall(SNESSetFromOptions(snes_water));
607 
608   if (viewX) {
609     PetscCall(PetscPrintf(PETSC_COMM_WORLD,"Water Solution:\n"));
610     PetscCall(VecView(X,PETSC_VIEWER_STDOUT_WORLD));
611   }
612   PetscCall(PetscLogStagePop());
613 
614   /* (4) Solve */
615   /*-----------*/
616   PetscCall(PetscLogStageRegister("SNES Solve",&stage[3]));
617   PetscCall(PetscLogStagePush(stage[3]));
618   user.it = 0;
619   reason  = SNES_DIVERGED_DTOL;
620   while (user.it < it_max && (PetscInt)reason<0) {
621 #if 0
622     user.subsnes_id = 0;
623     PetscCall(SNESSolve(snes_power,NULL,X));
624 
625     user.subsnes_id = 1;
626     PetscCall(SNESSolve(snes_water,NULL,X));
627 #endif
628     user.subsnes_id = Nsubnet;
629     PetscCall(SNESSolve(snes,NULL,X));
630 
631     PetscCall(SNESGetConvergedReason(snes,&reason));
632     user.it++;
633   }
634   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"Coupled_SNES converged in %" PetscInt_FMT " iterations\n",user.it));
635   if (viewX) {
636     PetscCall(PetscPrintf(PETSC_COMM_WORLD,"Final Solution:\n"));
637     PetscCall(VecView(X,PETSC_VIEWER_STDOUT_WORLD));
638   }
639   PetscCall(PetscLogStagePop());
640 
641   /* Free objects */
642   /* -------------*/
643   PetscCall(VecDestroy(&X));
644   PetscCall(VecDestroy(&F));
645   PetscCall(DMRestoreLocalVector(networkdm,&user.localXold));
646 
647   PetscCall(SNESDestroy(&snes));
648   PetscCall(MatDestroy(&Jac));
649   PetscCall(SNESDestroy(&snes_power));
650   PetscCall(SNESDestroy(&snes_water));
651 
652   PetscCall(DMDestroy(&networkdm));
653   PetscCall(PetscFinalize());
654   return 0;
655 }
656 
657 /*TEST
658 
659    build:
660      requires: !complex double defined(PETSC_HAVE_ATTRIBUTEALIGNED)
661      depends: power/PFReadData.c power/pffunctions.c water/waterreaddata.c water/waterfunctions.c
662 
663    test:
664       args: -coupled_snes_converged_reason -options_left no -viewDM
665       localrunfiles: ex1options power/case9.m water/sample1.inp
666       output_file: output/ex1.out
667 
668    test:
669       suffix: 2
670       nsize: 3
671       args: -coupled_snes_converged_reason -options_left no -petscpartitioner_type parmetis
672       localrunfiles: ex1options power/case9.m water/sample1.inp
673       output_file: output/ex1_2.out
674       requires: parmetis
675 
676 #   test:
677 #      suffix: 3
678 #      nsize: 3
679 #      args: -coupled_snes_converged_reason -options_left no -distribute false
680 #      localrunfiles: ex1options power/case9.m water/sample1.inp
681 #      output_file: output/ex1_2.out
682 
683    test:
684       suffix: 4
685       nsize: 4
686       args: -coupled_snes_converged_reason -options_left no -petscpartitioner_type simple -viewDM
687       localrunfiles: ex1options power/case9.m water/sample1.inp
688       output_file: output/ex1_4.out
689 
690 TEST*/
691