xref: /petsc/src/snes/tutorials/network/ex1.c (revision 8fb5bd83c3955fefcf33a54e3bb66920a9fa884b)
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   PetscCall(PetscInitialize(&argc,&argv,"ex1options",help));
325   PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD,&rank));
326   PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD,&size));
327 
328   /* (1) Read Data - Only rank 0 reads the data */
329   /*--------------------------------------------*/
330   PetscCall(PetscLogStageRegister("Read Data",&stage[0]));
331   PetscCall(PetscLogStagePush(stage[0]));
332 
333   for (i=0; i<Nsubnet; i++) {
334     numVertices[i] = 0;
335     numEdges[i]    = 0;
336   }
337 
338   /* All processes READ THE DATA FOR THE FIRST SUBNETWORK: Electric Power Grid */
339   /* Used for shared vertex, because currently the coupling info must be available in all processes!!! */
340   PetscCall(PetscOptionsGetString(NULL,NULL,"-pfdata",pfdata_file,PETSC_MAX_PATH_LEN-1,NULL));
341   PetscCall(PetscNew(&pfdata));
342   PetscCall(PFReadMatPowerData(pfdata,pfdata_file));
343   if (rank == 0) {
344     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));
345   }
346   Sbase = pfdata->sbase;
347   if (rank == 0) { /* proc[0] will create Electric Power Grid */
348     numEdges[0]    = pfdata->nbranch;
349     numVertices[0] = pfdata->nbus;
350 
351     PetscCall(PetscMalloc1(2*numEdges[0],&edgelist_power));
352     PetscCall(GetListofEdges_Power(pfdata,edgelist_power));
353   }
354   /* Broadcast power Sbase to all processors */
355   PetscCallMPI(MPI_Bcast(&Sbase,1,MPIU_SCALAR,0,PETSC_COMM_WORLD));
356   appctx_power->Sbase     = Sbase;
357   appctx_power->jac_error = PETSC_FALSE;
358   /* If external option activated. Introduce error in jacobian */
359   PetscCall(PetscOptionsHasName(NULL,NULL, "-jac_error", &appctx_power->jac_error));
360 
361   /* All processes READ THE DATA FOR THE SECOND SUBNETWORK: Water */
362   /* Used for shared vertex, because currently the coupling info must be available in all processes!!! */
363   PetscCall(PetscNew(&waterdata));
364   PetscCall(PetscOptionsGetString(NULL,NULL,"-waterdata",waterdata_file,PETSC_MAX_PATH_LEN-1,NULL));
365   PetscCall(WaterReadData(waterdata,waterdata_file));
366   if (size == 1 || (size > 1 && rank == 1)) {
367     PetscCall(PetscCalloc1(2*waterdata->nedge,&edgelist_water));
368     PetscCall(GetListofEdges_Water(waterdata,edgelist_water));
369     numEdges[1]    = waterdata->nedge;
370     numVertices[1] = waterdata->nvertex;
371   }
372   PetscLogStagePop();
373 
374   /* (2) Create a network consist of two subnetworks */
375   /*-------------------------------------------------*/
376   PetscCall(PetscLogStageRegister("Net Setup",&stage[1]));
377   PetscCall(PetscLogStagePush(stage[1]));
378 
379   PetscCall(PetscOptionsGetBool(NULL,NULL,"-viewDM",&viewDM,NULL));
380   PetscCall(PetscOptionsGetBool(NULL,NULL,"-test",&test,NULL));
381   PetscCall(PetscOptionsGetBool(NULL,NULL,"-distribute",&distribute,NULL));
382 
383   /* Create an empty network object */
384   PetscCall(DMNetworkCreate(PETSC_COMM_WORLD,&networkdm));
385 
386   /* Register the components in the network */
387   PetscCall(DMNetworkRegisterComponent(networkdm,"branchstruct",sizeof(struct _p_EDGE_Power),&appctx_power->compkey_branch));
388   PetscCall(DMNetworkRegisterComponent(networkdm,"busstruct",sizeof(struct _p_VERTEX_Power),&appctx_power->compkey_bus));
389   PetscCall(DMNetworkRegisterComponent(networkdm,"genstruct",sizeof(struct _p_GEN),&appctx_power->compkey_gen));
390   PetscCall(DMNetworkRegisterComponent(networkdm,"loadstruct",sizeof(struct _p_LOAD),&appctx_power->compkey_load));
391 
392   PetscCall(DMNetworkRegisterComponent(networkdm,"edge_water",sizeof(struct _p_EDGE_Water),&appctx_water->compkey_edge));
393   PetscCall(DMNetworkRegisterComponent(networkdm,"vertex_water",sizeof(struct _p_VERTEX_Water),&appctx_water->compkey_vtx));
394 #if 0
395   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"power->compkey_branch %d\n",appctx_power->compkey_branch));
396   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"power->compkey_bus    %d\n",appctx_power->compkey_bus));
397   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"power->compkey_gen    %d\n",appctx_power->compkey_gen));
398   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"power->compkey_load   %d\n",appctx_power->compkey_load));
399   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"water->compkey_edge   %d\n",appctx_water->compkey_edge));
400   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"water->compkey_vtx    %d\n",appctx_water->compkey_vtx));
401 #endif
402   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]));
403   PetscCall(PetscSynchronizedFlush(PETSC_COMM_WORLD,PETSC_STDOUT));
404 
405   PetscCall(DMNetworkSetNumSubNetworks(networkdm,PETSC_DECIDE,Nsubnet));
406   PetscCall(DMNetworkAddSubnetwork(networkdm,"power",numEdges[0],edgelist_power,&power_netnum));
407   PetscCall(DMNetworkAddSubnetwork(networkdm,"water",numEdges[1],edgelist_water,&water_netnum));
408 
409   /* vertex subnet[0].4 shares with vertex subnet[1].0 */
410   power_svtx = 4; water_svtx = 0;
411   PetscCall(DMNetworkAddSharedVertices(networkdm,power_netnum,water_netnum,1,&power_svtx,&water_svtx));
412 
413   /* Set up the network layout */
414   PetscCall(DMNetworkLayoutSetUp(networkdm));
415 
416   /* ADD VARIABLES AND COMPONENTS FOR THE POWER SUBNETWORK */
417   /*-------------------------------------------------------*/
418   genj = 0; loadj = 0;
419   PetscCall(DMNetworkGetSubnetwork(networkdm,power_netnum,&nv,&ne,&vtx,&edges));
420 
421   for (i = 0; i < ne; i++) {
422     PetscCall(DMNetworkAddComponent(networkdm,edges[i],appctx_power->compkey_branch,&pfdata->branch[i],0));
423   }
424 
425   for (i = 0; i < nv; i++) {
426     PetscCall(DMNetworkIsSharedVertex(networkdm,vtx[i],&flg));
427     if (flg) continue;
428 
429     PetscCall(DMNetworkAddComponent(networkdm,vtx[i],appctx_power->compkey_bus,&pfdata->bus[i],2));
430     if (pfdata->bus[i].ngen) {
431       for (j = 0; j < pfdata->bus[i].ngen; j++) {
432         PetscCall(DMNetworkAddComponent(networkdm,vtx[i],appctx_power->compkey_gen,&pfdata->gen[genj++],0));
433       }
434     }
435     if (pfdata->bus[i].nload) {
436       for (j=0; j < pfdata->bus[i].nload; j++) {
437         PetscCall(DMNetworkAddComponent(networkdm,vtx[i],appctx_power->compkey_load,&pfdata->load[loadj++],0));
438       }
439     }
440   }
441 
442   /* ADD VARIABLES AND COMPONENTS FOR THE WATER SUBNETWORK */
443   /*-------------------------------------------------------*/
444   PetscCall(DMNetworkGetSubnetwork(networkdm,water_netnum,&nv,&ne,&vtx,&edges));
445   for (i = 0; i < ne; i++) {
446     PetscCall(DMNetworkAddComponent(networkdm,edges[i],appctx_water->compkey_edge,&waterdata->edge[i],0));
447   }
448 
449   for (i = 0; i < nv; i++) {
450     PetscCall(DMNetworkIsSharedVertex(networkdm,vtx[i],&flg));
451     if (flg) continue;
452 
453     PetscCall(DMNetworkAddComponent(networkdm,vtx[i],appctx_water->compkey_vtx,&waterdata->vertex[i],1));
454   }
455 
456   /* 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 */
457   /*----------------------------------------------------------------------------------------------------------------------------*/
458   PetscCall(DMNetworkGetSharedVertices(networkdm,&nv,&vtx));
459   for (i = 0; i < nv; i++) {
460     /* power */
461     PetscCall(DMNetworkAddComponent(networkdm,vtx[i],appctx_power->compkey_bus,&pfdata->bus[4],2));
462     /* bus[4] is a load, add its component */
463     PetscCall(DMNetworkAddComponent(networkdm,vtx[i],appctx_power->compkey_load,&pfdata->load[0],0));
464 
465     /* water */
466     PetscCall(DMNetworkAddComponent(networkdm,vtx[i],appctx_water->compkey_vtx,&waterdata->vertex[0],1));
467   }
468 
469   /* Set up DM for use */
470   PetscCall(DMSetUp(networkdm));
471   if (viewDM) {
472     PetscCall(PetscPrintf(PETSC_COMM_WORLD,"\nAfter DMSetUp, DMView:\n"));
473     PetscCall(DMView(networkdm,PETSC_VIEWER_STDOUT_WORLD));
474   }
475 
476   /* Free user objects */
477   PetscCall(PetscFree(edgelist_power));
478   PetscCall(PetscFree(pfdata->bus));
479   PetscCall(PetscFree(pfdata->gen));
480   PetscCall(PetscFree(pfdata->branch));
481   PetscCall(PetscFree(pfdata->load));
482   PetscCall(PetscFree(pfdata));
483 
484   PetscCall(PetscFree(edgelist_water));
485   PetscCall(PetscFree(waterdata->vertex));
486   PetscCall(PetscFree(waterdata->edge));
487   PetscCall(PetscFree(waterdata));
488 
489   /* Re-distribute networkdm to multiple processes for better job balance */
490   if (size >1 && distribute) {
491     PetscCall(DMNetworkDistribute(&networkdm,0));
492     if (viewDM) {
493       PetscCall(PetscPrintf(PETSC_COMM_WORLD,"\nAfter DMNetworkDistribute, DMView:\n"));
494       PetscCall(DMView(networkdm,PETSC_VIEWER_STDOUT_WORLD));
495     }
496   }
497 
498   /* Test DMNetworkGetSubnetwork() and DMNetworkGetSubnetworkSharedVertices() */
499   if (test) {
500     PetscInt  v,gidx;
501     PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
502     for (i=0; i<Nsubnet; i++) {
503       PetscCall(DMNetworkGetSubnetwork(networkdm,i,&nv,&ne,&vtx,&edges));
504       PetscCall(PetscPrintf(PETSC_COMM_SELF,"[%d] After distribute, subnet[%" PetscInt_FMT "] ne %" PetscInt_FMT ", nv %" PetscInt_FMT "\n",rank,i,ne,nv));
505       PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
506 
507       for (v=0; v<nv; v++) {
508         PetscCall(DMNetworkIsGhostVertex(networkdm,vtx[v],&ghost));
509         PetscCall(DMNetworkGetGlobalVertexIndex(networkdm,vtx[v],&gidx));
510         PetscCall(PetscPrintf(PETSC_COMM_SELF,"[%d] subnet[%" PetscInt_FMT "] v %" PetscInt_FMT " %" PetscInt_FMT "; ghost %d\n",rank,i,vtx[v],gidx,ghost));
511       }
512       PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
513     }
514     PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
515 
516     PetscCall(DMNetworkGetSharedVertices(networkdm,&nv,&vtx));
517     PetscCall(PetscPrintf(PETSC_COMM_SELF,"[%d] After distribute, num of shared vertices nsv = %" PetscInt_FMT "\n",rank,nv));
518     for (v=0; v<nv; v++) {
519       PetscCall(DMNetworkGetGlobalVertexIndex(networkdm,vtx[v],&gidx));
520       PetscCall(PetscPrintf(PETSC_COMM_SELF,"[%d] sv %" PetscInt_FMT ", gidx=%" PetscInt_FMT "\n",rank,vtx[v],gidx));
521     }
522     PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
523   }
524 
525   /* Create solution vector X */
526   PetscCall(DMCreateGlobalVector(networkdm,&X));
527   PetscCall(VecDuplicate(X,&F));
528   PetscCall(DMGetLocalVector(networkdm,&user.localXold));
529   PetscLogStagePop();
530 
531   /* (3) Setup Solvers */
532   /*-------------------*/
533   PetscCall(PetscOptionsGetBool(NULL,NULL,"-viewJ",&viewJ,NULL));
534   PetscCall(PetscOptionsGetBool(NULL,NULL,"-viewX",&viewX,NULL));
535 
536   PetscCall(PetscLogStageRegister("SNES Setup",&stage[2]));
537   PetscCall(PetscLogStagePush(stage[2]));
538 
539   PetscCall(SetInitialGuess(networkdm,X,&user));
540 
541   /* Create coupled snes */
542   /*-------------------- */
543   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"SNES_coupled setup ......\n"));
544   user.subsnes_id = Nsubnet;
545   PetscCall(SNESCreate(PETSC_COMM_WORLD,&snes));
546   PetscCall(SNESSetDM(snes,networkdm));
547   PetscCall(SNESSetOptionsPrefix(snes,"coupled_"));
548   PetscCall(SNESSetFunction(snes,F,FormFunction,&user));
549   PetscCall(SNESMonitorSet(snes,UserMonitor,&user,NULL));
550   PetscCall(SNESSetFromOptions(snes));
551 
552   if (viewJ) {
553     /* View Jac structure */
554     PetscCall(SNESGetJacobian(snes,&Jac,NULL,NULL,NULL));
555     PetscCall(MatView(Jac,PETSC_VIEWER_DRAW_WORLD));
556   }
557 
558   if (viewX) {
559     PetscCall(PetscPrintf(PETSC_COMM_WORLD,"Solution:\n"));
560     PetscCall(VecView(X,PETSC_VIEWER_STDOUT_WORLD));
561   }
562 
563   if (viewJ) {
564     /* View assembled Jac */
565     PetscCall(MatView(Jac,PETSC_VIEWER_DRAW_WORLD));
566   }
567 
568   /* Create snes_power */
569   /*-------------------*/
570   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"SNES_power setup ......\n"));
571 
572   user.subsnes_id = 0;
573   PetscCall(SNESCreate(PETSC_COMM_WORLD,&snes_power));
574   PetscCall(SNESSetDM(snes_power,networkdm));
575   PetscCall(SNESSetOptionsPrefix(snes_power,"power_"));
576   PetscCall(SNESSetFunction(snes_power,F,FormFunction,&user));
577   PetscCall(SNESMonitorSet(snes_power,UserMonitor,&user,NULL));
578 
579   /* Use user-provide Jacobian */
580   PetscCall(DMCreateMatrix(networkdm,&Jac));
581   PetscCall(SNESSetJacobian(snes_power,Jac,Jac,FormJacobian_subPower,&user));
582   PetscCall(SNESSetFromOptions(snes_power));
583 
584   if (viewX) {
585     PetscCall(PetscPrintf(PETSC_COMM_WORLD,"Power Solution:\n"));
586     PetscCall(VecView(X,PETSC_VIEWER_STDOUT_WORLD));
587   }
588   if (viewJ) {
589     PetscCall(PetscPrintf(PETSC_COMM_WORLD,"Power Jac:\n"));
590     PetscCall(SNESGetJacobian(snes_power,&Jac,NULL,NULL,NULL));
591     PetscCall(MatView(Jac,PETSC_VIEWER_DRAW_WORLD));
592     /* PetscCall(MatView(Jac,PETSC_VIEWER_STDOUT_WORLD)); */
593   }
594 
595   /* Create snes_water */
596   /*-------------------*/
597   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"SNES_water setup......\n"));
598 
599   user.subsnes_id = 1;
600   PetscCall(SNESCreate(PETSC_COMM_WORLD,&snes_water));
601   PetscCall(SNESSetDM(snes_water,networkdm));
602   PetscCall(SNESSetOptionsPrefix(snes_water,"water_"));
603   PetscCall(SNESSetFunction(snes_water,F,FormFunction,&user));
604   PetscCall(SNESMonitorSet(snes_water,UserMonitor,&user,NULL));
605   PetscCall(SNESSetFromOptions(snes_water));
606 
607   if (viewX) {
608     PetscCall(PetscPrintf(PETSC_COMM_WORLD,"Water Solution:\n"));
609     PetscCall(VecView(X,PETSC_VIEWER_STDOUT_WORLD));
610   }
611   PetscCall(PetscLogStagePop());
612 
613   /* (4) Solve */
614   /*-----------*/
615   PetscCall(PetscLogStageRegister("SNES Solve",&stage[3]));
616   PetscCall(PetscLogStagePush(stage[3]));
617   user.it = 0;
618   reason  = SNES_DIVERGED_DTOL;
619   while (user.it < it_max && (PetscInt)reason<0) {
620 #if 0
621     user.subsnes_id = 0;
622     PetscCall(SNESSolve(snes_power,NULL,X));
623 
624     user.subsnes_id = 1;
625     PetscCall(SNESSolve(snes_water,NULL,X));
626 #endif
627     user.subsnes_id = Nsubnet;
628     PetscCall(SNESSolve(snes,NULL,X));
629 
630     PetscCall(SNESGetConvergedReason(snes,&reason));
631     user.it++;
632   }
633   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"Coupled_SNES converged in %" PetscInt_FMT " iterations\n",user.it));
634   if (viewX) {
635     PetscCall(PetscPrintf(PETSC_COMM_WORLD,"Final Solution:\n"));
636     PetscCall(VecView(X,PETSC_VIEWER_STDOUT_WORLD));
637   }
638   PetscCall(PetscLogStagePop());
639 
640   /* Free objects */
641   /* -------------*/
642   PetscCall(VecDestroy(&X));
643   PetscCall(VecDestroy(&F));
644   PetscCall(DMRestoreLocalVector(networkdm,&user.localXold));
645 
646   PetscCall(SNESDestroy(&snes));
647   PetscCall(MatDestroy(&Jac));
648   PetscCall(SNESDestroy(&snes_power));
649   PetscCall(SNESDestroy(&snes_water));
650 
651   PetscCall(DMDestroy(&networkdm));
652   PetscCall(PetscFinalize());
653   return 0;
654 }
655 
656 /*TEST
657 
658    build:
659      requires: !complex double defined(PETSC_HAVE_ATTRIBUTEALIGNED)
660      depends: power/PFReadData.c power/pffunctions.c water/waterreaddata.c water/waterfunctions.c
661 
662    test:
663       args: -coupled_snes_converged_reason -options_left no -viewDM
664       localrunfiles: ex1options power/case9.m water/sample1.inp
665       output_file: output/ex1.out
666 
667    test:
668       suffix: 2
669       nsize: 3
670       args: -coupled_snes_converged_reason -options_left no -petscpartitioner_type parmetis
671       localrunfiles: ex1options power/case9.m water/sample1.inp
672       output_file: output/ex1_2.out
673       requires: parmetis
674 
675 #   test:
676 #      suffix: 3
677 #      nsize: 3
678 #      args: -coupled_snes_converged_reason -options_left no -distribute false
679 #      localrunfiles: ex1options power/case9.m water/sample1.inp
680 #      output_file: output/ex1_2.out
681 
682    test:
683       suffix: 4
684       nsize: 4
685       args: -coupled_snes_converged_reason -options_left no -petscpartitioner_type simple -viewDM
686       localrunfiles: ex1options power/case9.m water/sample1.inp
687       output_file: output/ex1_4.out
688 
689 TEST*/
690