Date: 2026-05-01T06:11:53+00:00

Actual source code: ex2.c

      1: static char help[] = "This example is based on ex1.c, but generates a random network of chosen sizes and parameters. \n\
      2:   Usage: -n determines number of nodes. The nonnegative seed can be specified with the flag -seed, otherwise the program generates a random seed.\n\n";

      4: #include <petscdmnetwork.h>
      5: #include <petscksp.h>
      6: #include <time.h>

      8: typedef struct {
      9:   PetscInt    id;  /* node id */
     10:   PetscScalar inj; /* current injection (A) */
     11:   PetscBool   gr;  /* grounded ? */
     12: } Node;

     14: typedef struct {
     15:   PetscInt    id;  /* branch id */
     16:   PetscScalar r;   /* resistance (ohms) */
     17:   PetscScalar bat; /* battery (V) */
     18: } Branch;

     20: typedef struct Edge {
     21:   PetscInt     n;
     22:   PetscInt     i;
     23:   PetscInt     j;
     24:   struct Edge *next;
     25: } Edge;

     27: PetscReal findDistance(PetscReal x1, PetscReal x2, PetscReal y1, PetscReal y2)
     28: {
     29:   return PetscSqrtReal(PetscPowReal(x2 - x1, 2.0) + PetscPowReal(y2 - y1, 2.0));
     30: }

     32: /*
     33:   The algorithm for network formation is based on the paper:
     34:   Routing of Multipoint Connections, Bernard M. Waxman. 1988
     35: */

     37: PetscErrorCode random_network(PetscInt nvertex, PetscInt *pnbranch, Node **pnode, Branch **pbranch, PetscInt **pedgelist, PetscInt seed)
     38: {
     39:   PetscInt    i, j, nedges = 0;
     40:   PetscInt   *edgelist;
     41:   PetscInt    nbat, ncurr, fr, to;
     42:   PetscReal  *x, *y, value, xmax = 10.0; /* generate points in square */
     43:   PetscReal   maxdist = 0.0, dist, alpha, beta, prob;
     44:   PetscRandom rnd;
     45:   Branch     *branch;
     46:   Node       *node;
     47:   Edge       *head = NULL, *nnew = NULL, *aux = NULL;

     49:   PetscFunctionBeginUser;
     50:   PetscCall(PetscRandomCreate(PETSC_COMM_SELF, &rnd));
     51:   PetscCall(PetscRandomSetFromOptions(rnd));

     53:   PetscCall(PetscRandomSetSeed(rnd, seed));
     54:   PetscCall(PetscRandomSeed(rnd));

     56:   /* These parameters might be modified for experimentation */
     57:   nbat  = (PetscInt)(0.1 * nvertex);
     58:   ncurr = (PetscInt)(0.1 * nvertex);
     59:   alpha = 0.6;
     60:   beta  = 0.2;

     62:   PetscCall(PetscMalloc2(nvertex, &x, nvertex, &y));

     64:   PetscCall(PetscRandomSetInterval(rnd, 0.0, xmax));
     65:   for (i = 0; i < nvertex; i++) {
     66:     PetscCall(PetscRandomGetValueReal(rnd, &x[i]));
     67:     PetscCall(PetscRandomGetValueReal(rnd, &y[i]));
     68:   }

     70:   /* find maximum distance */
     71:   for (i = 0; i < nvertex; i++) {
     72:     for (j = 0; j < nvertex; j++) {
     73:       dist = findDistance(x[i], x[j], y[i], y[j]);
     74:       if (dist >= maxdist) maxdist = dist;
     75:     }
     76:   }

     78:   PetscCall(PetscRandomSetInterval(rnd, 0.0, 1.0));
     79:   for (i = 0; i < nvertex; i++) {
     80:     for (j = 0; j < nvertex; j++) {
     81:       if (j != i) {
     82:         dist = findDistance(x[i], x[j], y[i], y[j]);
     83:         prob = beta * PetscExpScalar(-dist / (maxdist * alpha));
     84:         PetscCall(PetscRandomGetValueReal(rnd, &value));
     85:         if (value <= prob) {
     86:           PetscCall(PetscMalloc1(1, &nnew));
     87:           if (head == NULL) {
     88:             head       = nnew;
     89:             head->next = NULL;
     90:             head->n    = nedges;
     91:             head->i    = i;
     92:             head->j    = j;
     93:           } else {
     94:             aux        = head;
     95:             head       = nnew;
     96:             head->n    = nedges;
     97:             head->next = aux;
     98:             head->i    = i;
     99:             head->j    = j;
    100:           }
    101:           nedges += 1;
    102:         }
    103:       }
    104:     }
    105:   }

    107:   PetscCall(PetscMalloc1(2 * nedges, &edgelist));

    109:   for (aux = head; aux; aux = aux->next) {
    110:     edgelist[(aux->n) * 2]     = aux->i;
    111:     edgelist[(aux->n) * 2 + 1] = aux->j;
    112:   }

    114:   aux = head;
    115:   while (aux != NULL) {
    116:     nnew = aux;
    117:     aux  = aux->next;
    118:     PetscCall(PetscFree(nnew));
    119:   }

    121:   PetscCall(PetscCalloc2(nvertex, &node, nedges, &branch));

    123:   for (i = 0; i < nvertex; i++) {
    124:     node[i].id  = i;
    125:     node[i].inj = 0;
    126:     node[i].gr  = PETSC_FALSE;
    127:   }

    129:   for (i = 0; i < nedges; i++) {
    130:     branch[i].id  = i;
    131:     branch[i].r   = 1.0;
    132:     branch[i].bat = 0;
    133:   }

    135:   /* Chose random node as ground voltage */
    136:   PetscCall(PetscRandomSetInterval(rnd, 0.0, nvertex));
    137:   PetscCall(PetscRandomGetValueReal(rnd, &value));
    138:   node[(int)value].gr = PETSC_TRUE;

    140:   /* Create random current and battery injectionsa */
    141:   for (i = 0; i < ncurr; i++) {
    142:     PetscCall(PetscRandomSetInterval(rnd, 0.0, nvertex));
    143:     PetscCall(PetscRandomGetValueReal(rnd, &value));
    144:     fr = edgelist[(int)value * 2];
    145:     to = edgelist[(int)value * 2 + 1];
    146:     node[fr].inj += 1.0;
    147:     node[to].inj -= 1.0;
    148:   }

    150:   for (i = 0; i < nbat; i++) {
    151:     PetscCall(PetscRandomSetInterval(rnd, 0.0, nedges));
    152:     PetscCall(PetscRandomGetValueReal(rnd, &value));
    153:     branch[(int)value].bat += 1.0;
    154:   }

    156:   PetscCall(PetscFree2(x, y));
    157:   PetscCall(PetscRandomDestroy(&rnd));

    159:   /* assign pointers */
    160:   *pnbranch  = nedges;
    161:   *pedgelist = edgelist;
    162:   *pbranch   = branch;
    163:   *pnode     = node;
    164:   PetscFunctionReturn(PETSC_SUCCESS);
    165: }

    167: PetscErrorCode FormOperator(DM networkdm, Mat A, Vec b)
    168: {
    169:   Vec             localb;
    170:   Branch         *branch;
    171:   Node           *node;
    172:   PetscInt        e, v, vStart, vEnd, eStart, eEnd;
    173:   PetscInt        lofst, lofst_to, lofst_fr, row[2], col[6];
    174:   PetscBool       ghost;
    175:   const PetscInt *cone;
    176:   PetscScalar    *barr, val[6];

    178:   PetscFunctionBegin;
    179:   PetscCall(DMGetLocalVector(networkdm, &localb));
    180:   PetscCall(VecSet(b, 0.0));
    181:   PetscCall(VecSet(localb, 0.0));
    182:   PetscCall(MatZeroEntries(A));

    184:   PetscCall(VecGetArray(localb, &barr));

    186:   /*
    187:     We can define the current as a "edge characteristic" and the voltage
    188:     and the voltage as a "vertex characteristic". With that, we can iterate
    189:     the list of edges and vertices, query the associated voltages and currents
    190:     and use them to write the Kirchoff equations.
    191:   */

    193:   /* Branch equations: i/r + uj - ui = battery */
    194:   PetscCall(DMNetworkGetEdgeRange(networkdm, &eStart, &eEnd));
    195:   for (e = 0; e < eEnd; e++) {
    196:     PetscCall(DMNetworkGetComponent(networkdm, e, 0, NULL, (void **)&branch, NULL));
    197:     PetscCall(DMNetworkGetLocalVecOffset(networkdm, e, ALL_COMPONENTS, &lofst));

    199:     PetscCall(DMNetworkGetConnectedVertices(networkdm, e, &cone));
    200:     PetscCall(DMNetworkGetLocalVecOffset(networkdm, cone[0], ALL_COMPONENTS, &lofst_fr));
    201:     PetscCall(DMNetworkGetLocalVecOffset(networkdm, cone[1], ALL_COMPONENTS, &lofst_to));

    203:     barr[lofst] = branch->bat;

    205:     row[0] = lofst;
    206:     col[0] = lofst;
    207:     val[0] = 1;
    208:     col[1] = lofst_to;
    209:     val[1] = 1;
    210:     col[2] = lofst_fr;
    211:     val[2] = -1;
    212:     PetscCall(MatSetValuesLocal(A, 1, row, 3, col, val, ADD_VALUES));

    214:     /* from node */
    215:     PetscCall(DMNetworkGetComponent(networkdm, cone[0], 0, NULL, (void **)&node, NULL));

    217:     if (!node->gr) {
    218:       row[0] = lofst_fr;
    219:       col[0] = lofst;
    220:       val[0] = 1;
    221:       PetscCall(MatSetValuesLocal(A, 1, row, 1, col, val, ADD_VALUES));
    222:     }

    224:     /* to node */
    225:     PetscCall(DMNetworkGetComponent(networkdm, cone[1], 0, NULL, (void **)&node, NULL));

    227:     if (!node->gr) {
    228:       row[0] = lofst_to;
    229:       col[0] = lofst;
    230:       val[0] = -1;
    231:       PetscCall(MatSetValuesLocal(A, 1, row, 1, col, val, ADD_VALUES));
    232:     }
    233:   }

    235:   PetscCall(DMNetworkGetVertexRange(networkdm, &vStart, &vEnd));
    236:   for (v = vStart; v < vEnd; v++) {
    237:     PetscCall(DMNetworkIsGhostVertex(networkdm, v, &ghost));
    238:     if (!ghost) {
    239:       PetscCall(DMNetworkGetComponent(networkdm, v, 0, NULL, (void **)&node, NULL));
    240:       PetscCall(DMNetworkGetLocalVecOffset(networkdm, v, ALL_COMPONENTS, &lofst));

    242:       if (node->gr) {
    243:         row[0] = lofst;
    244:         col[0] = lofst;
    245:         val[0] = 1;
    246:         PetscCall(MatSetValuesLocal(A, 1, row, 1, col, val, ADD_VALUES));
    247:       } else {
    248:         barr[lofst] -= node->inj;
    249:       }
    250:     }
    251:   }

    253:   PetscCall(VecRestoreArray(localb, &barr));

    255:   PetscCall(DMLocalToGlobalBegin(networkdm, localb, ADD_VALUES, b));
    256:   PetscCall(DMLocalToGlobalEnd(networkdm, localb, ADD_VALUES, b));
    257:   PetscCall(DMRestoreLocalVector(networkdm, &localb));

    259:   PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
    260:   PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
    261:   PetscFunctionReturn(PETSC_SUCCESS);
    262: }

    264: int main(int argc, char **argv)
    265: {
    266:   PetscInt      i, nbranch = 0, eStart, eEnd, vStart, vEnd;
    267:   PetscInt      seed = 0, nnode = 0;
    268:   PetscMPIInt   size, rank;
    269:   DM            networkdm;
    270:   Vec           x, b;
    271:   Mat           A;
    272:   KSP           ksp;
    273:   PetscInt     *edgelist = NULL;
    274:   PetscInt      componentkey[2];
    275:   Node         *node;
    276:   Branch       *branch;
    277:   PetscLogStage stage[3];

    279:   PetscFunctionBeginUser;
    280:   PetscCall(PetscInitialize(&argc, &argv, NULL, help));
    281:   PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD, &rank));
    282:   PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));

    284:   PetscCall(PetscOptionsGetInt(NULL, NULL, "-seed", &seed, NULL));

    286:   PetscCall(PetscLogStageRegister("Network Creation", &stage[0]));
    287:   PetscCall(PetscLogStageRegister("DMNetwork data structures", &stage[1]));
    288:   PetscCall(PetscLogStageRegister("KSP", &stage[2]));

    290:   PetscCall(PetscLogStagePush(stage[0]));
    291:   /* "read" data only for processor 0 */
    292:   if (rank == 0) {
    293:     nnode = 100;
    294:     PetscCall(PetscOptionsGetInt(NULL, NULL, "-n", &nnode, NULL));
    295:     PetscCall(random_network(nnode, &nbranch, &node, &branch, &edgelist, seed));
    296:   }
    297:   PetscCall(PetscLogStagePop());

    299:   PetscCall(PetscLogStagePush(stage[1]));
    300:   PetscCall(DMNetworkCreate(PETSC_COMM_WORLD, &networkdm));
    301:   PetscCall(DMNetworkRegisterComponent(networkdm, "nstr", sizeof(Node), &componentkey[0]));
    302:   PetscCall(DMNetworkRegisterComponent(networkdm, "bsrt", sizeof(Branch), &componentkey[1]));

    304:   /* Set number of nodes/edges and edge connectivity */
    305:   PetscCall(DMNetworkSetNumSubNetworks(networkdm, PETSC_DECIDE, 1));
    306:   PetscCall(DMNetworkAddSubnetwork(networkdm, "", nbranch, edgelist, NULL));

    308:   /* Set up the network layout */
    309:   PetscCall(DMNetworkLayoutSetUp(networkdm));

    311:   /* Add network components (physical parameters of nodes and branches) and num of variables */
    312:   if (rank == 0) {
    313:     PetscCall(DMNetworkGetEdgeRange(networkdm, &eStart, &eEnd));
    314:     for (i = eStart; i < eEnd; i++) PetscCall(DMNetworkAddComponent(networkdm, i, componentkey[1], &branch[i - eStart], 1));

    316:     PetscCall(DMNetworkGetVertexRange(networkdm, &vStart, &vEnd));
    317:     for (i = vStart; i < vEnd; i++) PetscCall(DMNetworkAddComponent(networkdm, i, componentkey[0], &node[i - vStart], 1));
    318:   }

    320:   /* Network partitioning and distribution of data */
    321:   PetscCall(DMSetUp(networkdm));
    322:   PetscCall(DMNetworkDistribute(&networkdm, 0));
    323:   PetscCall(DMNetworkAssembleGraphStructures(networkdm));

    325:   /* We don't use these data structures anymore since they have been copied to networkdm */
    326:   if (rank == 0) {
    327:     PetscCall(PetscFree(edgelist));
    328:     PetscCall(PetscFree2(node, branch));
    329:   }

    331:   /* Create vectors and matrix */
    332:   PetscCall(DMCreateGlobalVector(networkdm, &x));
    333:   PetscCall(VecDuplicate(x, &b));
    334:   PetscCall(DMCreateMatrix(networkdm, &A));

    336:   PetscCall(PetscLogStagePop());

    338:   PetscCall(PetscLogStagePush(stage[2]));
    339:   /* Assembly system of equations */
    340:   PetscCall(FormOperator(networkdm, A, b));

    342:   /* Solve linear system: A x = b */
    343:   PetscCall(KSPCreate(PETSC_COMM_WORLD, &ksp));
    344:   PetscCall(KSPSetOperators(ksp, A, A));
    345:   PetscCall(KSPSetFromOptions(ksp));
    346:   PetscCall(KSPSolve(ksp, b, x));

    348:   PetscCall(PetscLogStagePop());

    350:   /* Free work space */
    351:   PetscCall(VecDestroy(&x));
    352:   PetscCall(VecDestroy(&b));
    353:   PetscCall(MatDestroy(&A));
    354:   PetscCall(KSPDestroy(&ksp));
    355:   PetscCall(DMDestroy(&networkdm));
    356:   PetscCall(PetscFinalize());
    357:   return 0;
    358: }

    360: /*TEST

    362:    build:
    363:       requires: !single double defined(PETSC_HAVE_ATTRIBUTEALIGNED) 64bitptr

    365:    test:
    366:       args: -ksp_converged_reason

    368:    test:
    369:       suffix: 2
    370:       nsize: 2
    371:       args: -petscpartitioner_type simple -pc_type asm -sub_pc_type ilu -ksp_converged_reason

    373:    test:
    374:       suffix: 3
    375:       nsize: 4
    376:       args: -petscpartitioner_type simple -pc_type asm -sub_pc_type lu -sub_pc_factor_shift_type nonzero -ksp_converged_reason

    378:    test:
    379:       suffix: graphindex
    380:       args: -n 20 -vertex_global_section_view -edge_global_section_view

    382:    test:
    383:       suffix: graphindex_2
    384:       nsize: 2
    385:       args: -petscpartitioner_type simple -n 20 -vertex_global_section_view -edge_global_section_view

    387: TEST*/