Date: 2026-07-30T21:14:22+00:00

Actual source code: power2.c

      1: static char help[] = "This example demonstrates the use of DMNetwork interface with subnetworks for solving a nonlinear electric power grid problem.\n\
      2:                       The available solver options are in the poweroptions file and the data files are in the datafiles directory.\n\
      3:                       The data file format used is from the MatPower package (http://www.pserc.cornell.edu//matpower/).\n\
      4:                       This example shows the use of subnetwork feature in DMNetwork. It creates duplicates of the same network which are treated as subnetworks.\n\
      5:                       Run this program: mpiexec -n <n> ./pf2\n\
      6:                       mpiexec -n <n> ./pf2 \n";

      8: #include "power.h"
      9: #include <petscdmnetwork.h>

     11: PetscErrorCode FormFunction_Subnet(DM networkdm, Vec localX, Vec localF, PetscInt nv, PetscInt ne, const PetscInt *vtx, const PetscInt *edges, void *appctx)
     12: {
     13:   UserCtx_Power     *User = (UserCtx_Power *)appctx;
     14:   PetscInt           e, v, vfrom, vto;
     15:   const PetscScalar *xarr;
     16:   PetscScalar       *farr;
     17:   PetscInt           offsetfrom, offsetto, offset;

     19:   PetscFunctionBegin;
     20:   PetscCall(VecGetArrayRead(localX, &xarr));
     21:   PetscCall(VecGetArray(localF, &farr));

     23:   for (v = 0; v < nv; v++) {
     24:     PetscInt     i, j, key;
     25:     PetscScalar  Vm;
     26:     PetscScalar  Sbase = User->Sbase;
     27:     VERTEX_Power bus   = NULL;
     28:     GEN          gen;
     29:     LOAD         load;
     30:     PetscBool    ghostvtex;
     31:     PetscInt     numComps;
     32:     void        *component;

     34:     PetscCall(DMNetworkIsGhostVertex(networkdm, vtx[v], &ghostvtex));
     35:     PetscCall(DMNetworkGetNumComponents(networkdm, vtx[v], &numComps));
     36:     PetscCall(DMNetworkGetLocalVecOffset(networkdm, vtx[v], ALL_COMPONENTS, &offset));
     37:     for (j = 0; j < numComps; j++) {
     38:       PetscCall(DMNetworkGetComponent(networkdm, vtx[v], j, &key, &component, NULL));
     39:       if (key == 1) {
     40:         PetscInt        nconnedges;
     41:         const PetscInt *connedges;

     43:         bus = (VERTEX_Power)component;
     44:         /* Handle reference bus constrained dofs */
     45:         if (bus->ide == REF_BUS || bus->ide == ISOLATED_BUS) {
     46:           farr[offset]     = xarr[offset] - bus->va * PETSC_PI / 180.0;
     47:           farr[offset + 1] = xarr[offset + 1] - bus->vm;
     48:           break;
     49:         }

     51:         if (!ghostvtex) {
     52:           Vm = xarr[offset + 1];

     54:           /* Shunt injections */
     55:           farr[offset] += Vm * Vm * bus->gl / Sbase;
     56:           if (bus->ide != PV_BUS) farr[offset + 1] += -Vm * Vm * bus->bl / Sbase;
     57:         }

     59:         PetscCall(DMNetworkGetSupportingEdges(networkdm, vtx[v], &nconnedges, &connedges));
     60:         for (i = 0; i < nconnedges; i++) {
     61:           EDGE_Power      branch;
     62:           PetscInt        keye;
     63:           PetscScalar     Gff, Bff, Gft, Bft, Gtf, Btf, Gtt, Btt;
     64:           const PetscInt *cone;
     65:           PetscScalar     Vmf, Vmt, thetaf, thetat, thetaft, thetatf;

     67:           e = connedges[i];
     68:           PetscCall(DMNetworkGetComponent(networkdm, e, 0, &keye, (void **)&branch, NULL));
     69:           if (!branch->status) continue;
     70:           Gff = branch->yff[0];
     71:           Bff = branch->yff[1];
     72:           Gft = branch->yft[0];
     73:           Bft = branch->yft[1];
     74:           Gtf = branch->ytf[0];
     75:           Btf = branch->ytf[1];
     76:           Gtt = branch->ytt[0];
     77:           Btt = branch->ytt[1];

     79:           PetscCall(DMNetworkGetConnectedVertices(networkdm, e, &cone));
     80:           vfrom = cone[0];
     81:           vto   = cone[1];

     83:           PetscCall(DMNetworkGetLocalVecOffset(networkdm, vfrom, ALL_COMPONENTS, &offsetfrom));
     84:           PetscCall(DMNetworkGetLocalVecOffset(networkdm, vto, ALL_COMPONENTS, &offsetto));

     86:           thetaf  = xarr[offsetfrom];
     87:           Vmf     = xarr[offsetfrom + 1];
     88:           thetat  = xarr[offsetto];
     89:           Vmt     = xarr[offsetto + 1];
     90:           thetaft = thetaf - thetat;
     91:           thetatf = thetat - thetaf;

     93:           if (vfrom == vtx[v]) {
     94:             farr[offsetfrom] += Gff * Vmf * Vmf + Vmf * Vmt * (Gft * PetscCosScalar(thetaft) + Bft * PetscSinScalar(thetaft));
     95:             farr[offsetfrom + 1] += -Bff * Vmf * Vmf + Vmf * Vmt * (-Bft * PetscCosScalar(thetaft) + Gft * PetscSinScalar(thetaft));
     96:           } else {
     97:             farr[offsetto] += Gtt * Vmt * Vmt + Vmt * Vmf * (Gtf * PetscCosScalar(thetatf) + Btf * PetscSinScalar(thetatf));
     98:             farr[offsetto + 1] += -Btt * Vmt * Vmt + Vmt * Vmf * (-Btf * PetscCosScalar(thetatf) + Gtf * PetscSinScalar(thetatf));
     99:           }
    100:         }
    101:       } else if (key == 2) {
    102:         if (!ghostvtex) {
    103:           gen = (GEN)component;
    104:           if (!gen->status) continue;
    105:           farr[offset] += -gen->pg / Sbase;
    106:           farr[offset + 1] += -gen->qg / Sbase;
    107:         }
    108:       } else if (key == 3) {
    109:         if (!ghostvtex) {
    110:           load = (LOAD)component;
    111:           farr[offset] += load->pl / Sbase;
    112:           farr[offset + 1] += load->ql / Sbase;
    113:         }
    114:       }
    115:     }
    116:     if (bus && bus->ide == PV_BUS) farr[offset + 1] = xarr[offset + 1] - bus->vm;
    117:   }
    118:   PetscCall(VecRestoreArrayRead(localX, &xarr));
    119:   PetscCall(VecRestoreArray(localF, &farr));
    120:   PetscFunctionReturn(PETSC_SUCCESS);
    121: }

    123: PetscErrorCode FormFunction(SNES snes, Vec X, Vec F, void *appctx)
    124: {
    125:   DM              networkdm;
    126:   Vec             localX, localF;
    127:   PetscInt        nv, ne;
    128:   const PetscInt *vtx, *edges;

    130:   PetscFunctionBegin;
    131:   PetscCall(SNESGetDM(snes, &networkdm));
    132:   PetscCall(DMGetLocalVector(networkdm, &localX));
    133:   PetscCall(DMGetLocalVector(networkdm, &localF));
    134:   PetscCall(VecSet(F, 0.0));

    136:   PetscCall(DMGlobalToLocalBegin(networkdm, X, INSERT_VALUES, localX));
    137:   PetscCall(DMGlobalToLocalEnd(networkdm, X, INSERT_VALUES, localX));

    139:   PetscCall(DMGlobalToLocalBegin(networkdm, F, INSERT_VALUES, localF));
    140:   PetscCall(DMGlobalToLocalEnd(networkdm, F, INSERT_VALUES, localF));

    142:   /* Form Function for first subnetwork */
    143:   PetscCall(DMNetworkGetSubnetwork(networkdm, 0, &nv, &ne, &vtx, &edges));
    144:   PetscCall(FormFunction_Subnet(networkdm, localX, localF, nv, ne, vtx, edges, appctx));

    146:   /* Form Function for second subnetwork */
    147:   PetscCall(DMNetworkGetSubnetwork(networkdm, 1, &nv, &ne, &vtx, &edges));
    148:   PetscCall(FormFunction_Subnet(networkdm, localX, localF, nv, ne, vtx, edges, appctx));

    150:   PetscCall(DMRestoreLocalVector(networkdm, &localX));

    152:   PetscCall(DMLocalToGlobalBegin(networkdm, localF, ADD_VALUES, F));
    153:   PetscCall(DMLocalToGlobalEnd(networkdm, localF, ADD_VALUES, F));
    154:   PetscCall(DMRestoreLocalVector(networkdm, &localF));
    155:   PetscFunctionReturn(PETSC_SUCCESS);
    156: }

    158: PetscErrorCode FormJacobian_Subnet(DM networkdm, Vec localX, Mat J, Mat Jpre, PetscInt nv, PetscInt ne, const PetscInt *vtx, const PetscInt *edges, void *appctx)
    159: {
    160:   UserCtx_Power     *User = (UserCtx_Power *)appctx;
    161:   PetscInt           e, v, vfrom, vto;
    162:   const PetscScalar *xarr;
    163:   PetscInt           offsetfrom, offsetto, goffsetfrom, goffsetto;
    164:   PetscInt           row[2], col[8];
    165:   PetscScalar        values[8];

    167:   PetscFunctionBegin;
    168:   PetscCall(VecGetArrayRead(localX, &xarr));

    170:   for (v = 0; v < nv; v++) {
    171:     PetscInt     i, j, key;
    172:     PetscInt     offset, goffset;
    173:     PetscScalar  Vm;
    174:     PetscScalar  Sbase = User->Sbase;
    175:     VERTEX_Power bus;
    176:     PetscBool    ghostvtex;
    177:     PetscInt     numComps;
    178:     void        *component;

    180:     PetscCall(DMNetworkIsGhostVertex(networkdm, vtx[v], &ghostvtex));
    181:     PetscCall(DMNetworkGetNumComponents(networkdm, vtx[v], &numComps));
    182:     for (j = 0; j < numComps; j++) {
    183:       PetscCall(DMNetworkGetLocalVecOffset(networkdm, vtx[v], ALL_COMPONENTS, &offset));
    184:       PetscCall(DMNetworkGetGlobalVecOffset(networkdm, vtx[v], ALL_COMPONENTS, &goffset));
    185:       PetscCall(DMNetworkGetComponent(networkdm, vtx[v], j, &key, &component, NULL));
    186:       if (key == 1) {
    187:         PetscInt        nconnedges;
    188:         const PetscInt *connedges;

    190:         bus = (VERTEX_Power)component;
    191:         if (!ghostvtex) {
    192:           /* Handle reference bus constrained dofs */
    193:           if (bus->ide == REF_BUS || bus->ide == ISOLATED_BUS) {
    194:             row[0]    = goffset;
    195:             row[1]    = goffset + 1;
    196:             col[0]    = goffset;
    197:             col[1]    = goffset + 1;
    198:             col[2]    = goffset;
    199:             col[3]    = goffset + 1;
    200:             values[0] = 1.0;
    201:             values[1] = 0.0;
    202:             values[2] = 0.0;
    203:             values[3] = 1.0;
    204:             PetscCall(MatSetValues(J, 2, row, 2, col, values, ADD_VALUES));
    205:             break;
    206:           }

    208:           Vm = xarr[offset + 1];

    210:           /* Shunt injections */
    211:           row[0]    = goffset;
    212:           row[1]    = goffset + 1;
    213:           col[0]    = goffset;
    214:           col[1]    = goffset + 1;
    215:           values[0] = values[1] = values[2] = values[3] = 0.0;
    216:           if (bus->ide != PV_BUS) {
    217:             values[1] = 2.0 * Vm * bus->gl / Sbase;
    218:             values[3] = -2.0 * Vm * bus->bl / Sbase;
    219:           }
    220:           PetscCall(MatSetValues(J, 2, row, 2, col, values, ADD_VALUES));
    221:         }

    223:         PetscCall(DMNetworkGetSupportingEdges(networkdm, vtx[v], &nconnedges, &connedges));
    224:         for (i = 0; i < nconnedges; i++) {
    225:           EDGE_Power      branch;
    226:           VERTEX_Power    busf, bust;
    227:           PetscInt        keyf, keyt;
    228:           PetscScalar     Gff, Bff, Gft, Bft, Gtf, Btf, Gtt, Btt;
    229:           const PetscInt *cone;
    230:           PetscScalar     Vmf, Vmt, thetaf, thetat, thetaft, thetatf;

    232:           e = connedges[i];
    233:           PetscCall(DMNetworkGetComponent(networkdm, e, 0, &key, (void **)&branch, NULL));
    234:           if (!branch->status) continue;

    236:           Gff = branch->yff[0];
    237:           Bff = branch->yff[1];
    238:           Gft = branch->yft[0];
    239:           Bft = branch->yft[1];
    240:           Gtf = branch->ytf[0];
    241:           Btf = branch->ytf[1];
    242:           Gtt = branch->ytt[0];
    243:           Btt = branch->ytt[1];

    245:           PetscCall(DMNetworkGetConnectedVertices(networkdm, e, &cone));
    246:           vfrom = cone[0];
    247:           vto   = cone[1];

    249:           PetscCall(DMNetworkGetLocalVecOffset(networkdm, vfrom, ALL_COMPONENTS, &offsetfrom));
    250:           PetscCall(DMNetworkGetLocalVecOffset(networkdm, vto, ALL_COMPONENTS, &offsetto));
    251:           PetscCall(DMNetworkGetGlobalVecOffset(networkdm, vfrom, ALL_COMPONENTS, &goffsetfrom));
    252:           PetscCall(DMNetworkGetGlobalVecOffset(networkdm, vto, ALL_COMPONENTS, &goffsetto));

    254:           if (goffsetto < 0) goffsetto = -goffsetto - 1;

    256:           thetaf  = xarr[offsetfrom];
    257:           Vmf     = xarr[offsetfrom + 1];
    258:           thetat  = xarr[offsetto];
    259:           Vmt     = xarr[offsetto + 1];
    260:           thetaft = thetaf - thetat;
    261:           thetatf = thetat - thetaf;

    263:           PetscCall(DMNetworkGetComponent(networkdm, vfrom, 0, &keyf, (void **)&busf, NULL));
    264:           PetscCall(DMNetworkGetComponent(networkdm, vto, 0, &keyt, (void **)&bust, NULL));

    266:           if (vfrom == vtx[v]) {
    267:             if (busf->ide != REF_BUS) {
    268:               /*    farr[offsetfrom]   += Gff*Vmf*Vmf + Vmf*Vmt*(Gft*PetscCosScalar(thetaft) + Bft*PetscSinScalar(thetaft));  */
    269:               row[0]    = goffsetfrom;
    270:               col[0]    = goffsetfrom;
    271:               col[1]    = goffsetfrom + 1;
    272:               col[2]    = goffsetto;
    273:               col[3]    = goffsetto + 1;
    274:               values[0] = Vmf * Vmt * (Gft * -PetscSinScalar(thetaft) + Bft * PetscCosScalar(thetaft));            /* df_dthetaf */
    275:               values[1] = 2.0 * Gff * Vmf + Vmt * (Gft * PetscCosScalar(thetaft) + Bft * PetscSinScalar(thetaft)); /* df_dVmf */
    276:               values[2] = Vmf * Vmt * (Gft * PetscSinScalar(thetaft) + Bft * -PetscCosScalar(thetaft));            /* df_dthetat */
    277:               values[3] = Vmf * (Gft * PetscCosScalar(thetaft) + Bft * PetscSinScalar(thetaft));                   /* df_dVmt */

    279:               PetscCall(MatSetValues(J, 1, row, 4, col, values, ADD_VALUES));
    280:             }
    281:             if (busf->ide != PV_BUS && busf->ide != REF_BUS) {
    282:               row[0] = goffsetfrom + 1;
    283:               col[0] = goffsetfrom;
    284:               col[1] = goffsetfrom + 1;
    285:               col[2] = goffsetto;
    286:               col[3] = goffsetto + 1;
    287:               /*    farr[offsetfrom+1] += -Bff*Vmf*Vmf + Vmf*Vmt*(-Bft*PetscCosScalar(thetaft) + Gft*PetscSinScalar(thetaft)); */
    288:               values[0] = Vmf * Vmt * (Bft * PetscSinScalar(thetaft) + Gft * PetscCosScalar(thetaft));
    289:               values[1] = -2.0 * Bff * Vmf + Vmt * (-Bft * PetscCosScalar(thetaft) + Gft * PetscSinScalar(thetaft));
    290:               values[2] = Vmf * Vmt * (-Bft * PetscSinScalar(thetaft) + Gft * -PetscCosScalar(thetaft));
    291:               values[3] = Vmf * (-Bft * PetscCosScalar(thetaft) + Gft * PetscSinScalar(thetaft));

    293:               PetscCall(MatSetValues(J, 1, row, 4, col, values, ADD_VALUES));
    294:             }
    295:           } else {
    296:             if (bust->ide != REF_BUS) {
    297:               row[0] = goffsetto;
    298:               col[0] = goffsetto;
    299:               col[1] = goffsetto + 1;
    300:               col[2] = goffsetfrom;
    301:               col[3] = goffsetfrom + 1;
    302:               /*    farr[offsetto]   += Gtt*Vmt*Vmt + Vmt*Vmf*(Gtf*PetscCosScalar(thetatf) + Btf*PetscSinScalar(thetatf)); */
    303:               values[0] = Vmt * Vmf * (Gtf * -PetscSinScalar(thetatf) + Btf * PetscCosScalar(thetaft));            /* df_dthetat */
    304:               values[1] = 2.0 * Gtt * Vmt + Vmf * (Gtf * PetscCosScalar(thetatf) + Btf * PetscSinScalar(thetatf)); /* df_dVmt */
    305:               values[2] = Vmt * Vmf * (Gtf * PetscSinScalar(thetatf) + Btf * -PetscCosScalar(thetatf));            /* df_dthetaf */
    306:               values[3] = Vmt * (Gtf * PetscCosScalar(thetatf) + Btf * PetscSinScalar(thetatf));                   /* df_dVmf */

    308:               PetscCall(MatSetValues(J, 1, row, 4, col, values, ADD_VALUES));
    309:             }
    310:             if (bust->ide != PV_BUS && bust->ide != REF_BUS) {
    311:               row[0] = goffsetto + 1;
    312:               col[0] = goffsetto;
    313:               col[1] = goffsetto + 1;
    314:               col[2] = goffsetfrom;
    315:               col[3] = goffsetfrom + 1;
    316:               /*    farr[offsetto+1] += -Btt*Vmt*Vmt + Vmt*Vmf*(-Btf*PetscCosScalar(thetatf) + Gtf*PetscSinScalar(thetatf)); */
    317:               values[0] = Vmt * Vmf * (Btf * PetscSinScalar(thetatf) + Gtf * PetscCosScalar(thetatf));
    318:               values[1] = -2.0 * Btt * Vmt + Vmf * (-Btf * PetscCosScalar(thetatf) + Gtf * PetscSinScalar(thetatf));
    319:               values[2] = Vmt * Vmf * (-Btf * PetscSinScalar(thetatf) + Gtf * -PetscCosScalar(thetatf));
    320:               values[3] = Vmt * (-Btf * PetscCosScalar(thetatf) + Gtf * PetscSinScalar(thetatf));

    322:               PetscCall(MatSetValues(J, 1, row, 4, col, values, ADD_VALUES));
    323:             }
    324:           }
    325:         }
    326:         if (!ghostvtex && bus->ide == PV_BUS) {
    327:           row[0]    = goffset + 1;
    328:           col[0]    = goffset + 1;
    329:           values[0] = 1.0;
    330:           PetscCall(MatSetValues(J, 1, row, 1, col, values, ADD_VALUES));
    331:         }
    332:       }
    333:     }
    334:   }
    335:   PetscCall(VecRestoreArrayRead(localX, &xarr));
    336:   PetscFunctionReturn(PETSC_SUCCESS);
    337: }

    339: PetscErrorCode FormJacobian(SNES snes, Vec X, Mat J, Mat Jpre, void *appctx)
    340: {
    341:   DM              networkdm;
    342:   Vec             localX;
    343:   PetscInt        ne, nv;
    344:   const PetscInt *vtx, *edges;

    346:   PetscFunctionBegin;
    347:   PetscCall(MatZeroEntries(J));

    349:   PetscCall(SNESGetDM(snes, &networkdm));
    350:   PetscCall(DMGetLocalVector(networkdm, &localX));

    352:   PetscCall(DMGlobalToLocalBegin(networkdm, X, INSERT_VALUES, localX));
    353:   PetscCall(DMGlobalToLocalEnd(networkdm, X, INSERT_VALUES, localX));

    355:   /* Form Jacobian for first subnetwork */
    356:   PetscCall(DMNetworkGetSubnetwork(networkdm, 0, &nv, &ne, &vtx, &edges));
    357:   PetscCall(FormJacobian_Subnet(networkdm, localX, J, Jpre, nv, ne, vtx, edges, appctx));

    359:   /* Form Jacobian for second subnetwork */
    360:   PetscCall(DMNetworkGetSubnetwork(networkdm, 1, &nv, &ne, &vtx, &edges));
    361:   PetscCall(FormJacobian_Subnet(networkdm, localX, J, Jpre, nv, ne, vtx, edges, appctx));

    363:   PetscCall(DMRestoreLocalVector(networkdm, &localX));

    365:   PetscCall(MatAssemblyBegin(J, MAT_FINAL_ASSEMBLY));
    366:   PetscCall(MatAssemblyEnd(J, MAT_FINAL_ASSEMBLY));
    367:   PetscFunctionReturn(PETSC_SUCCESS);
    368: }

    370: PetscErrorCode SetInitialValues_Subnet(DM networkdm, Vec localX, PetscInt nv, PetscInt ne, const PetscInt *vtx, const PetscInt *edges, void *appctx)
    371: {
    372:   VERTEX_Power bus;
    373:   PetscInt     i;
    374:   GEN          gen;
    375:   PetscBool    ghostvtex;
    376:   PetscScalar *xarr;
    377:   PetscInt     key, numComps, j, offset;
    378:   void        *component;

    380:   PetscFunctionBegin;
    381:   PetscCall(VecGetArray(localX, &xarr));
    382:   for (i = 0; i < nv; i++) {
    383:     PetscCall(DMNetworkIsGhostVertex(networkdm, vtx[i], &ghostvtex));
    384:     if (ghostvtex) continue;

    386:     PetscCall(DMNetworkGetLocalVecOffset(networkdm, vtx[i], ALL_COMPONENTS, &offset));
    387:     PetscCall(DMNetworkGetNumComponents(networkdm, vtx[i], &numComps));
    388:     for (j = 0; j < numComps; j++) {
    389:       PetscCall(DMNetworkGetComponent(networkdm, vtx[i], j, &key, &component, NULL));
    390:       if (key == 1) {
    391:         bus              = (VERTEX_Power)component;
    392:         xarr[offset]     = bus->va * PETSC_PI / 180.0;
    393:         xarr[offset + 1] = bus->vm;
    394:       } else if (key == 2) {
    395:         gen = (GEN)component;
    396:         if (!gen->status) continue;
    397:         xarr[offset + 1] = gen->vs;
    398:         break;
    399:       }
    400:     }
    401:   }
    402:   PetscCall(VecRestoreArray(localX, &xarr));
    403:   PetscFunctionReturn(PETSC_SUCCESS);
    404: }

    406: PetscErrorCode SetInitialValues(DM networkdm, Vec X, void *appctx)
    407: {
    408:   PetscInt        nv, ne;
    409:   const PetscInt *vtx, *edges;
    410:   Vec             localX;

    412:   PetscFunctionBegin;
    413:   PetscCall(DMGetLocalVector(networkdm, &localX));

    415:   PetscCall(VecSet(X, 0.0));
    416:   PetscCall(DMGlobalToLocalBegin(networkdm, X, INSERT_VALUES, localX));
    417:   PetscCall(DMGlobalToLocalEnd(networkdm, X, INSERT_VALUES, localX));

    419:   /* Set initial guess for first subnetwork */
    420:   PetscCall(DMNetworkGetSubnetwork(networkdm, 0, &nv, &ne, &vtx, &edges));
    421:   PetscCall(SetInitialValues_Subnet(networkdm, localX, nv, ne, vtx, edges, appctx));

    423:   /* Set initial guess for second subnetwork */
    424:   PetscCall(DMNetworkGetSubnetwork(networkdm, 1, &nv, &ne, &vtx, &edges));
    425:   PetscCall(SetInitialValues_Subnet(networkdm, localX, nv, ne, vtx, edges, appctx));

    427:   PetscCall(DMLocalToGlobalBegin(networkdm, localX, ADD_VALUES, X));
    428:   PetscCall(DMLocalToGlobalEnd(networkdm, localX, ADD_VALUES, X));
    429:   PetscCall(DMRestoreLocalVector(networkdm, &localX));
    430:   PetscFunctionReturn(PETSC_SUCCESS);
    431: }

    433: int main(int argc, char **argv)
    434: {
    435:   char            pfdata_file[PETSC_MAX_PATH_LEN] = "case9.m";
    436:   PFDATA         *pfdata1, *pfdata2;
    437:   PetscInt        numEdges1 = 0, numEdges2 = 0;
    438:   PetscInt       *edgelist1 = NULL, *edgelist2 = NULL, componentkey[4];
    439:   DM              networkdm;
    440:   UserCtx_Power   User;
    441:   PetscLogStage   stage1, stage2;
    442:   PetscMPIInt     rank;
    443:   PetscInt        nsubnet = 2, nv, ne, i, j, genj, loadj;
    444:   const PetscInt *vtx, *edges;
    445:   Vec             X, F;
    446:   Mat             J;
    447:   SNES            snes;

    449:   PetscFunctionBeginUser;
    450:   PetscCall(PetscInitialize(&argc, &argv, "poweroptions", help));
    451:   PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD, &rank));
    452:   {
    453:     /* introduce the const crank so the clang static analyzer realizes that if it enters any of the if (crank) then it must have entered the first */
    454:     /* this is an experiment to see how the analyzer reacts */
    455:     const PetscMPIInt crank = rank;

    457:     /* Create an empty network object */
    458:     PetscCall(DMNetworkCreate(PETSC_COMM_WORLD, &networkdm));

    460:     /* Register the components in the network */
    461:     PetscCall(DMNetworkRegisterComponent(networkdm, "branchstruct", sizeof(struct _p_EDGE_Power), &componentkey[0]));
    462:     PetscCall(DMNetworkRegisterComponent(networkdm, "busstruct", sizeof(struct _p_VERTEX_Power), &componentkey[1]));
    463:     PetscCall(DMNetworkRegisterComponent(networkdm, "genstruct", sizeof(struct _p_GEN), &componentkey[2]));
    464:     PetscCall(DMNetworkRegisterComponent(networkdm, "loadstruct", sizeof(struct _p_LOAD), &componentkey[3]));

    466:     PetscCall(PetscLogStageRegister("Read Data", &stage1));
    467:     PetscCall(PetscLogStagePush(stage1));
    468:     /* READ THE DATA */
    469:     if (!crank) {
    470:       /* Only rank 0 reads the data */
    471:       PetscCall(PetscOptionsGetString(NULL, NULL, "-pfdata", pfdata_file, sizeof(pfdata_file), NULL));
    472:       /* HERE WE CREATE COPIES OF THE SAME NETWORK THAT WILL BE TREATED AS SUBNETWORKS */

    474:       /*    READ DATA FOR THE FIRST SUBNETWORK */
    475:       PetscCall(PetscNew(&pfdata1));
    476:       PetscCall(PFReadMatPowerData(pfdata1, pfdata_file));
    477:       User.Sbase = pfdata1->sbase;

    479:       numEdges1 = pfdata1->nbranch;
    480:       PetscCall(PetscMalloc1(2 * numEdges1, &edgelist1));
    481:       PetscCall(GetListofEdges_Power(pfdata1, edgelist1));

    483:       /*    READ DATA FOR THE SECOND SUBNETWORK */
    484:       PetscCall(PetscNew(&pfdata2));
    485:       PetscCall(PFReadMatPowerData(pfdata2, pfdata_file));
    486:       User.Sbase = pfdata2->sbase;

    488:       numEdges2 = pfdata2->nbranch;
    489:       PetscCall(PetscMalloc1(2 * numEdges2, &edgelist2));
    490:       PetscCall(GetListofEdges_Power(pfdata2, edgelist2));
    491:     }

    493:     PetscCall(PetscLogStagePop());
    494:     PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
    495:     PetscCall(PetscLogStageRegister("Create network", &stage2));
    496:     PetscCall(PetscLogStagePush(stage2));

    498:     /* Set number of nodes/edges and edge connectivity */
    499:     PetscCall(DMNetworkSetNumSubNetworks(networkdm, PETSC_DECIDE, nsubnet));
    500:     PetscCall(DMNetworkAddSubnetwork(networkdm, "", numEdges1, edgelist1, NULL));
    501:     PetscCall(DMNetworkAddSubnetwork(networkdm, "", numEdges2, edgelist2, NULL));

    503:     /* Set up the network layout */
    504:     PetscCall(DMNetworkLayoutSetUp(networkdm));

    506:     /* Add network components only process 0 has any data to add*/
    507:     if (!crank) {
    508:       genj  = 0;
    509:       loadj = 0;

    511:       /* ADD VARIABLES AND COMPONENTS FOR THE FIRST SUBNETWORK */
    512:       PetscCall(DMNetworkGetSubnetwork(networkdm, 0, &nv, &ne, &vtx, &edges));

    514:       for (i = 0; i < ne; i++) PetscCall(DMNetworkAddComponent(networkdm, edges[i], componentkey[0], &pfdata1->branch[i], 0));

    516:       for (i = 0; i < nv; i++) {
    517:         PetscCall(DMNetworkAddComponent(networkdm, vtx[i], componentkey[1], &pfdata1->bus[i], 2));
    518:         if (pfdata1->bus[i].ngen) {
    519:           for (j = 0; j < pfdata1->bus[i].ngen; j++) PetscCall(DMNetworkAddComponent(networkdm, vtx[i], componentkey[2], &pfdata1->gen[genj++], 0));
    520:         }
    521:         if (pfdata1->bus[i].nload) {
    522:           for (j = 0; j < pfdata1->bus[i].nload; j++) PetscCall(DMNetworkAddComponent(networkdm, vtx[i], componentkey[3], &pfdata1->load[loadj++], 0));
    523:         }
    524:       }

    526:       genj  = 0;
    527:       loadj = 0;

    529:       /* ADD VARIABLES AND COMPONENTS FOR THE SECOND SUBNETWORK */
    530:       PetscCall(DMNetworkGetSubnetwork(networkdm, 1, &nv, &ne, &vtx, &edges));

    532:       for (i = 0; i < ne; i++) PetscCall(DMNetworkAddComponent(networkdm, edges[i], componentkey[0], &pfdata2->branch[i], 0));

    534:       for (i = 0; i < nv; i++) {
    535:         PetscCall(DMNetworkAddComponent(networkdm, vtx[i], componentkey[1], &pfdata2->bus[i], 2));
    536:         if (pfdata2->bus[i].ngen) {
    537:           for (j = 0; j < pfdata2->bus[i].ngen; j++) PetscCall(DMNetworkAddComponent(networkdm, vtx[i], componentkey[2], &pfdata2->gen[genj++], 0));
    538:         }
    539:         if (pfdata2->bus[i].nload) {
    540:           for (j = 0; j < pfdata2->bus[i].nload; j++) PetscCall(DMNetworkAddComponent(networkdm, vtx[i], componentkey[3], &pfdata2->load[loadj++], 0));
    541:         }
    542:       }
    543:     }

    545:     /* Set up DM for use */
    546:     PetscCall(DMSetUp(networkdm));

    548:     if (!crank) {
    549:       PetscCall(PetscFree(edgelist1));
    550:       PetscCall(PetscFree(edgelist2));
    551:     }

    553:     if (!crank) {
    554:       PetscCall(PetscFree(pfdata1->bus));
    555:       PetscCall(PetscFree(pfdata1->gen));
    556:       PetscCall(PetscFree(pfdata1->branch));
    557:       PetscCall(PetscFree(pfdata1->load));
    558:       PetscCall(PetscFree(pfdata1));

    560:       PetscCall(PetscFree(pfdata2->bus));
    561:       PetscCall(PetscFree(pfdata2->gen));
    562:       PetscCall(PetscFree(pfdata2->branch));
    563:       PetscCall(PetscFree(pfdata2->load));
    564:       PetscCall(PetscFree(pfdata2));
    565:     }

    567:     /* Distribute networkdm to multiple processes */
    568:     PetscCall(DMNetworkDistribute(&networkdm, 0));

    570:     PetscCall(PetscLogStagePop());

    572:     /* Broadcast Sbase to all processors */
    573:     PetscCallMPI(MPI_Bcast(&User.Sbase, 1, MPIU_SCALAR, 0, PETSC_COMM_WORLD));

    575:     PetscCall(DMCreateGlobalVector(networkdm, &X));
    576:     PetscCall(VecDuplicate(X, &F));

    578:     PetscCall(DMCreateMatrix(networkdm, &J));
    579:     PetscCall(MatSetOption(J, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE));

    581:     PetscCall(SetInitialValues(networkdm, X, &User));

    583:     /* HOOK UP SOLVER */
    584:     PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));
    585:     PetscCall(SNESSetDM(snes, networkdm));
    586:     PetscCall(SNESSetFunction(snes, F, FormFunction, &User));
    587:     PetscCall(SNESSetJacobian(snes, J, J, FormJacobian, &User));
    588:     PetscCall(SNESSetFromOptions(snes));

    590:     PetscCall(SNESSolve(snes, NULL, X));
    591:     /* PetscCall(VecView(X,PETSC_VIEWER_STDOUT_WORLD)); */

    593:     PetscCall(VecDestroy(&X));
    594:     PetscCall(VecDestroy(&F));
    595:     PetscCall(MatDestroy(&J));

    597:     PetscCall(SNESDestroy(&snes));
    598:     PetscCall(DMDestroy(&networkdm));
    599:   }
    600:   PetscCall(PetscFinalize());
    601:   return 0;
    602: }

    604: /*TEST

    606:    build:
    607:      depends: PFReadData.c pffunctions.c
    608:      requires: !complex double defined(PETSC_HAVE_ATTRIBUTEALIGNED)

    610:    test:
    611:      args: -snes_rtol 1.e-3
    612:      localrunfiles: poweroptions case9.m
    613:      output_file: output/power_1.out

    615:    test:
    616:      suffix: 2
    617:      args: -snes_rtol 1.e-3 -petscpartitioner_type simple
    618:      nsize: 4
    619:      localrunfiles: poweroptions case9.m
    620:      output_file: output/power_1.out

    622: TEST*/