Actual source code: plexegads.c

      1: #include "petscsys.h"
      2: #include <petsc/private/dmpleximpl.h>
      3: #include <petsc/private/hashmapi.h>

      5: /* We need to understand how to natively parse STEP files. There seems to be only one open-source implementation of
      6:    the STEP parser contained in the OpenCASCADE package. It is enough to make a strong man weep:

      8:      https://github.com/tpaviot/oce/tree/master/src/STEPControl

     10:    The STEP, and inner EXPRESS, formats are ISO standards, so they are documented

     12:      https://stackoverflow.com/questions/26774037/documentation-or-specification-for-step-and-stp-files
     13:      http://stepmod.sourceforge.net/express_model_spec/

     15:    but again it seems that there has been a deliberate effort at obfuscation, probably to raise the bar for entrants.
     16: */

     18: #ifdef PETSC_HAVE_EGADS
     19: #include <petscdmplexegads.h>

     21: PETSC_INTERN PetscErrorCode DMSnapToGeomModel_EGADS_Internal(DM, PetscInt, ego, PetscInt, PetscInt, PetscInt, const PetscScalar[], PetscScalar[], PetscBool);
     22: PETSC_INTERN PetscErrorCode DMPlex_Geom_EDGE_XYZtoUV_Internal(const PetscScalar[], ego, const PetscScalar[], const PetscInt, const PetscInt, PetscScalar[], PetscBool);
     23: PETSC_INTERN PetscErrorCode DMPlex_Geom_FACE_XYZtoUV_Internal(const PetscScalar[], ego, const PetscScalar[], const PetscInt, const PetscInt, PetscScalar[], PetscBool);

     25: PetscErrorCode DMPlex_EGADS_GeomDecode_Internal(const PetscInt geomClass, const PetscInt geomType, char **retClass, char **retType)
     26: {
     27:   PetscFunctionBeginHot;
     28:   /* EGADS Object Type */
     29:   if (geomClass == CONTXT) *retClass = (char *)"CONTEXT";
     30:   if (geomClass == TRANSFORM) *retClass = (char *)"TRANSFORM";
     31:   if (geomClass == TESSELLATION) *retClass = (char *)"TESSELLATION";
     32:   if (geomClass == NIL) *retClass = (char *)"NIL";
     33:   if (geomClass == EMPTY) *retClass = (char *)"EMPTY";
     34:   if (geomClass == REFERENCE) *retClass = (char *)"REFERENCE";
     35:   if (geomClass == PCURVE) *retClass = (char *)"PCURVE";
     36:   if (geomClass == CURVE) *retClass = (char *)"CURVE";
     37:   if (geomClass == SURFACE) *retClass = (char *)"SURFACE";
     38:   if (geomClass == NODE) *retClass = (char *)"NODE";
     39:   if (geomClass == EDGE) *retClass = (char *)"EDGE";
     40:   if (geomClass == LOOP) *retClass = (char *)"LOOP";
     41:   if (geomClass == FACE) *retClass = (char *)"FACE";
     42:   if (geomClass == SHELL) *retClass = (char *)"SHELL";
     43:   if (geomClass == BODY) *retClass = (char *)"BODY";
     44:   if (geomClass == MODEL) *retClass = (char *)"MODEL";

     46:   /* PCURVES & CURVES */
     47:   if (geomClass == PCURVE || geomClass == CURVE) {
     48:     if (geomType == LINE) *retType = (char *)"LINE";
     49:     if (geomType == CIRCLE) *retType = (char *)"CIRCLE";
     50:     if (geomType == ELLIPSE) *retType = (char *)"ELLIPSE";
     51:     if (geomType == PARABOLA) *retType = (char *)"PARABOLA";
     52:     if (geomType == HYPERBOLA) *retType = (char *)"HYPERBOLA";
     53:     if (geomType == TRIMMED) *retType = (char *)"TRIMMED";
     54:     if (geomType == BEZIER) *retType = (char *)"BEZIER";
     55:     if (geomType == BSPLINE) *retType = (char *)"BSPLINE";
     56:     if (geomType == OFFSET) *retType = (char *)"OFFSET";
     57:   }

     59:   /* SURFACE */
     60:   if (geomClass == SURFACE) {
     61:     if (geomType == PLANE) *retType = (char *)"PLANE";
     62:     if (geomType == SPHERICAL) *retType = (char *)"SPHERICAL";
     63:     if (geomType == CYLINDRICAL) *retType = (char *)"CYLINDRICAL";
     64:     if (geomType == REVOLUTION) *retType = (char *)"REVOLUTION";
     65:     if (geomType == TOROIDAL) *retType = (char *)"TOROIDAL";
     66:     if (geomType == CONICAL) *retType = (char *)"CONICAL";
     67:     if (geomType == EXTRUSION) *retType = (char *)"EXTRUSION";
     68:     if (geomType == BEZIER) *retType = (char *)"BEZIER";
     69:     if (geomType == BSPLINE) *retType = (char *)"BSPLINE";
     70:   }

     72:   /* TOPOLOGY */
     73:   if (geomClass == NODE || geomClass == EDGE || geomClass == LOOP || geomClass == FACE || geomClass == SHELL || geomClass == BODY || geomClass == MODEL) {
     74:     if (geomType == SREVERSE) *retType = (char *)"SREVERSE";
     75:     if (geomType == NOMTYPE) *retType = (char *)"NOMTYPE";
     76:     if (geomType == SFORWARD && geomClass == FACE) *retType = (char *)"SFORWARD";
     77:     if (geomType == ONENODE && geomClass == EDGE) *retType = (char *)"ONENODE";
     78:     if (geomType == TWONODE) *retType = (char *)"TWONODE";
     79:     if (geomType == OPEN) *retType = (char *)"OPEN";
     80:     if (geomType == CLOSED) *retType = (char *)"CLOSED";
     81:     if (geomType == DEGENERATE) *retType = (char *)"DEGENERATE";
     82:     if (geomType == WIREBODY) *retType = (char *)"WIREBODY";
     83:     if (geomType == FACEBODY) *retType = (char *)"FACEBODY";
     84:     if (geomType == SHEETBODY) *retType = (char *)"SHEETBODY";
     85:     if (geomType == SOLIDBODY) *retType = (char *)"SOLIDBODY";
     86:   }
     87:   PetscFunctionReturn(PETSC_SUCCESS);
     88: }

     90: PetscErrorCode DMPlex_EGADS_EDGE_XYZtoUV_Internal(const PetscScalar coords[], ego obj, const PetscScalar range[], const PetscInt v, const PetscInt dE, PetscScalar paramsV[])
     91: {
     92:   //
     93:   //
     94:   // Depreciated. Changed all references to DMPlex_Geom_FACE_XYZtoUV_Internal()
     95:   //
     96:   //

     98:   PetscInt    loopCntr = 0;
     99:   PetscScalar dx, dy, dz, lambda, tolr, obj_old, obj_tmp, target;
    100:   PetscScalar delta, A, b;
    101:   PetscScalar ts[2], tt[2], eval[18], data[18];

    103:   PetscFunctionBeginHot;
    104:   /* Initialize Levenberg-Marquardt parameters */
    105:   lambda = 1.0;
    106:   tolr   = 1.0;
    107:   target = 1.0E-20;
    108:   ts[0]  = (range[0] + range[1]) / 2.;

    110:   while (tolr >= target) {
    111:     PetscCall(EG_evaluate(obj, ts, eval));
    112:     dx      = coords[v * dE + 0] - eval[0];
    113:     dy      = coords[v * dE + 1] - eval[1];
    114:     dz      = coords[v * dE + 2] - eval[2];
    115:     obj_old = dx * dx + dy * dy + dz * dz;

    117:     if (obj_old < target) {
    118:       tolr = obj_old;
    119:       break;
    120:     }

    122:     A = (eval[3] * eval[3] + eval[4] * eval[4] + eval[5] * eval[5]) * (1.0 + lambda);
    123:     if (A == 0.0) {
    124:       PetscCall(PetscPrintf(PETSC_COMM_SELF, "A = 0.0 \n"));
    125:       break;
    126:     }
    127:     b = eval[3] * dx + eval[4] * dy + eval[5] * dz;

    129:     /* Solve A*delta = b */
    130:     delta = b / A;

    132:     /* Find a temp (u,v) and associated objective function */
    133:     tt[0] = ts[0] + delta;
    134:     if (tt[0] < range[0]) {
    135:       tt[0] = range[0];
    136:       delta = tt[0] - ts[0];
    137:     }
    138:     if (tt[0] > range[1]) {
    139:       tt[0] = range[1];
    140:       delta = tt[0] - ts[0];
    141:     }

    143:     PetscCall(EG_evaluate(obj, tt, data));

    145:     obj_tmp = (coords[v * dE + 0] - data[0]) * (coords[v * dE + 0] - data[0]) + (coords[v * dE + 1] - data[1]) * (coords[v * dE + 1] - data[1]) + (coords[v * dE + 2] - data[2]) * (coords[v * dE + 2] - data[2]);

    147:     /* If step is better, accept it and halve lambda (making it more Newton-like) */
    148:     if (obj_tmp < obj_old) {
    149:       obj_old = obj_tmp;
    150:       ts[0]   = tt[0];
    151:       for (int jj = 0; jj < 18; ++jj) eval[jj] = data[jj];
    152:       lambda /= 2.0;
    153:       if (lambda < 1.0E-14) lambda = 1.0E-14;
    154:       if (obj_old < target) {
    155:         tolr = obj_old;
    156:         break;
    157:       }
    158:     } else {
    159:       /* Otherwise reject it and double lambda (making it more gradient-descent like) */
    160:       lambda *= 2.0;
    161:     }

    163:     if ((tt[0] == range[0]) || (tt[0] == range[1])) break;
    164:     if (fabs(delta) < target) {
    165:       tolr = obj_old;
    166:       break;
    167:     }

    169:     tolr = obj_old;

    171:     loopCntr += 1;
    172:     if (loopCntr > 100) break;
    173:   }
    174:   paramsV[v * 3 + 0] = ts[0];
    175:   paramsV[v * 3 + 1] = 0.;
    176:   PetscFunctionReturn(PETSC_SUCCESS);
    177: }

    179: PetscErrorCode DMPlex_Geom_EDGE_XYZtoUV_Internal(const PetscScalar coords[], ego obj, const PetscScalar range[], const PetscInt v, const PetscInt dE, PetscScalar paramsV[], PetscBool islite)
    180: {
    181:   PetscInt    loopCntr = 0;
    182:   PetscScalar dx, dy, dz, lambda, tolr, obj_old, obj_tmp, target;
    183:   PetscScalar delta, A, b;
    184:   PetscScalar ts[2], tt[2], eval[18], data[18];

    186:   PetscFunctionBeginHot;
    187:   /* Initialize Levenberg-Marquardt parameters */
    188:   lambda = 1.0;
    189:   tolr   = 1.0;
    190:   target = 1.0E-20;
    191:   ts[0]  = (range[0] + range[1]) / 2.;

    193:   while (tolr >= target) {
    194:     if (islite) PetscCall(EGlite_evaluate(obj, ts, eval));
    195:     else PetscCall(EG_evaluate(obj, ts, eval));

    197:     dx      = coords[v * dE + 0] - eval[0];
    198:     dy      = coords[v * dE + 1] - eval[1];
    199:     dz      = coords[v * dE + 2] - eval[2];
    200:     obj_old = dx * dx + dy * dy + dz * dz;

    202:     if (obj_old < target) {
    203:       tolr = obj_old;
    204:       break;
    205:     }

    207:     A = (eval[3] * eval[3] + eval[4] * eval[4] + eval[5] * eval[5]) * (1.0 + lambda);
    208:     if (A == 0.0) {
    209:       PetscCall(PetscPrintf(PETSC_COMM_SELF, "A = 0.0 \n"));
    210:       break;
    211:     }
    212:     b = eval[3] * dx + eval[4] * dy + eval[5] * dz;

    214:     /* Solve A*delta = b */
    215:     delta = b / A;

    217:     /* Find a temp (u,v) and associated objective function */
    218:     tt[0] = ts[0] + delta;
    219:     if (tt[0] < range[0]) {
    220:       tt[0] = range[0];
    221:       delta = tt[0] - ts[0];
    222:     }
    223:     if (tt[0] > range[1]) {
    224:       tt[0] = range[1];
    225:       delta = tt[0] - ts[0];
    226:     }

    228:     if (islite) PetscCall(EGlite_evaluate(obj, tt, data));
    229:     else PetscCall(EG_evaluate(obj, tt, data));

    231:     obj_tmp = (coords[v * dE + 0] - data[0]) * (coords[v * dE + 0] - data[0]) + (coords[v * dE + 1] - data[1]) * (coords[v * dE + 1] - data[1]) + (coords[v * dE + 2] - data[2]) * (coords[v * dE + 2] - data[2]);

    233:     /* If step is better, accept it and halve lambda (making it more Newton-like) */
    234:     if (obj_tmp < obj_old) {
    235:       obj_old = obj_tmp;
    236:       ts[0]   = tt[0];
    237:       for (int jj = 0; jj < 18; ++jj) eval[jj] = data[jj];
    238:       lambda /= 2.0;
    239:       if (lambda < 1.0E-14) lambda = 1.0E-14;
    240:       if (obj_old < target) {
    241:         tolr = obj_old;
    242:         break;
    243:       }
    244:     } else {
    245:       /* Otherwise reject it and double lambda (making it more gradient-descent like) */
    246:       lambda *= 2.0;
    247:     }

    249:     if ((tt[0] == range[0]) || (tt[0] == range[1])) break;
    250:     if (fabs(delta) < target) {
    251:       tolr = obj_old;
    252:       break;
    253:     }

    255:     tolr = obj_old;

    257:     loopCntr += 1;
    258:     if (loopCntr > 100) break;
    259:   }
    260:   paramsV[v * 3 + 0] = ts[0];
    261:   paramsV[v * 3 + 1] = 0.;
    262:   PetscFunctionReturn(PETSC_SUCCESS);
    263: }

    265: PetscErrorCode DMPlex_EGADS_FACE_XYZtoUV_Internal(const PetscScalar coords[], ego obj, const PetscScalar range[], const PetscInt v, const PetscInt dE, PetscScalar paramsV[])
    266: {
    267:   //
    268:   //
    269:   // Depreciated. Changed all references to DMPlex_Geom_FACE_XYZtoUV_Internal()
    270:   //
    271:   //

    273:   PetscInt    loopCntr = 0;
    274:   PetscScalar dx, dy, dz, lambda, tolr, denom, obj_old, obj_tmp, target;
    275:   PetscScalar uvs[2], uvt[2], delta[2], A[4], b[2], eval[18], data[18];

    277:   PetscFunctionBeginHot;
    278:   /* Initialize Levenberg-Marquardt parameters */
    279:   lambda = 1.0;
    280:   tolr   = 1.0;
    281:   target = 1.0E-20;
    282:   uvs[0] = (range[0] + range[1]) / 2.;
    283:   uvs[1] = (range[2] + range[3]) / 2.;

    285:   while (tolr >= target) {
    286:     PetscCall(EG_evaluate(obj, uvs, eval));
    287:     dx      = coords[v * dE + 0] - eval[0];
    288:     dy      = coords[v * dE + 1] - eval[1];
    289:     dz      = coords[v * dE + 2] - eval[2];
    290:     obj_old = dx * dx + dy * dy + dz * dz;

    292:     if (obj_old < target) {
    293:       tolr = obj_old;
    294:       break;
    295:     }

    297:     A[0] = (eval[3] * eval[3] + eval[4] * eval[4] + eval[5] * eval[5]) * (1.0 + lambda);
    298:     A[1] = eval[3] * eval[6] + eval[4] * eval[7] + eval[5] * eval[8];
    299:     A[2] = A[1];
    300:     A[3] = (eval[6] * eval[6] + eval[7] * eval[7] + eval[8] * eval[8]) * (1.0 + lambda);

    302:     b[0] = eval[3] * dx + eval[4] * dy + eval[5] * dz;
    303:     b[1] = eval[6] * dx + eval[7] * dy + eval[8] * dz;

    305:     /* Solve A*delta = b using Cramer's Rule */
    306:     denom = A[0] * A[3] - A[2] * A[1];
    307:     if (denom == 0.0) PetscCall(PetscPrintf(PETSC_COMM_SELF, "denom = 0.0 \n"));
    308:     delta[0] = (b[0] * A[3] - b[1] * A[1]) / denom;
    309:     delta[1] = (A[0] * b[1] - A[2] * b[0]) / denom;

    311:     /* Find a temp (u,v) and associated objective function */
    312:     uvt[0] = uvs[0] + delta[0];
    313:     uvt[1] = uvs[1] + delta[1];

    315:     if (uvt[0] < range[0]) {
    316:       uvt[0]   = range[0];
    317:       delta[0] = uvt[0] - uvs[0];
    318:     }
    319:     if (uvt[0] > range[1]) {
    320:       uvt[0]   = range[1];
    321:       delta[0] = uvt[0] - uvs[0];
    322:     }
    323:     if (uvt[1] < range[2]) {
    324:       uvt[1]   = range[2];
    325:       delta[1] = uvt[1] - uvs[1];
    326:     }
    327:     if (uvt[1] > range[3]) {
    328:       uvt[1]   = range[3];
    329:       delta[1] = uvt[1] - uvs[1];
    330:     }

    332:     PetscCall(EG_evaluate(obj, uvt, data));

    334:     obj_tmp = (coords[v * dE + 0] - data[0]) * (coords[v * dE + 0] - data[0]) + (coords[v * dE + 1] - data[1]) * (coords[v * dE + 1] - data[1]) + (coords[v * dE + 2] - data[2]) * (coords[v * dE + 2] - data[2]);

    336:     /* If step is better, accept it and halve lambda (making it more Newton-like) */
    337:     if (obj_tmp < obj_old) {
    338:       obj_old = obj_tmp;
    339:       uvs[0]  = uvt[0];
    340:       uvs[1]  = uvt[1];
    341:       for (int jj = 0; jj < 18; ++jj) eval[jj] = data[jj];
    342:       lambda /= 2.0;
    343:       if (lambda < 1.0E-14) lambda = 1.0E-14;
    344:       if (obj_old < target) {
    345:         tolr = obj_old;
    346:         break;
    347:       }
    348:     } else {
    349:       /* Otherwise reject it and double lambda (making it more gradient-descent like) */
    350:       lambda *= 2.0;
    351:     }

    353:     if (sqrt(delta[0] * delta[0] + delta[1] * delta[1]) < target) {
    354:       tolr = obj_old;
    355:       break;
    356:     }

    358:     tolr = obj_old;

    360:     loopCntr += 1;
    361:     if (loopCntr > 100) break;
    362:   }
    363:   paramsV[v * 3 + 0] = uvs[0];
    364:   paramsV[v * 3 + 1] = uvs[1];
    365:   PetscFunctionReturn(PETSC_SUCCESS);
    366: }

    368: PetscErrorCode DMPlex_Geom_FACE_XYZtoUV_Internal(const PetscScalar coords[], ego obj, const PetscScalar range[], const PetscInt v, const PetscInt dE, PetscScalar paramsV[], PetscBool islite)
    369: {
    370:   PetscInt    loopCntr = 0;
    371:   PetscScalar dx, dy, dz, lambda, tolr, denom, obj_old, obj_tmp, target;
    372:   PetscScalar uvs[2], uvt[2], delta[2], A[4], b[2], eval[18], data[18];

    374:   PetscFunctionBeginHot;
    375:   /* Initialize Levenberg-Marquardt parameters */
    376:   lambda = 1.0;
    377:   tolr   = 1.0;
    378:   target = 1.0E-20;
    379:   uvs[0] = (range[0] + range[1]) / 2.;
    380:   uvs[1] = (range[2] + range[3]) / 2.;

    382:   while (tolr >= target) {
    383:     if (islite) PetscCallEGADS(EGlite_evaluate, (obj, uvs, eval));
    384:     else PetscCallEGADS(EG_evaluate, (obj, uvs, eval));

    386:     dx      = coords[v * dE + 0] - eval[0];
    387:     dy      = coords[v * dE + 1] - eval[1];
    388:     dz      = coords[v * dE + 2] - eval[2];
    389:     obj_old = dx * dx + dy * dy + dz * dz;

    391:     if (obj_old < target) {
    392:       tolr = obj_old;
    393:       break;
    394:     }

    396:     A[0] = (eval[3] * eval[3] + eval[4] * eval[4] + eval[5] * eval[5]) * (1.0 + lambda);
    397:     A[1] = eval[3] * eval[6] + eval[4] * eval[7] + eval[5] * eval[8];
    398:     A[2] = A[1];
    399:     A[3] = (eval[6] * eval[6] + eval[7] * eval[7] + eval[8] * eval[8]) * (1.0 + lambda);

    401:     b[0] = eval[3] * dx + eval[4] * dy + eval[5] * dz;
    402:     b[1] = eval[6] * dx + eval[7] * dy + eval[8] * dz;

    404:     /* Solve A*delta = b using Cramer's Rule */
    405:     denom = A[0] * A[3] - A[2] * A[1];
    406:     if (denom == 0.0) PetscCall(PetscPrintf(PETSC_COMM_SELF, "denom = 0.0 \n"));
    407:     delta[0] = (b[0] * A[3] - b[1] * A[1]) / denom;
    408:     delta[1] = (A[0] * b[1] - A[2] * b[0]) / denom;

    410:     /* Find a temp (u,v) and associated objective function */
    411:     uvt[0] = uvs[0] + delta[0];
    412:     uvt[1] = uvs[1] + delta[1];

    414:     if (uvt[0] < range[0]) {
    415:       uvt[0]   = range[0];
    416:       delta[0] = uvt[0] - uvs[0];
    417:     }
    418:     if (uvt[0] > range[1]) {
    419:       uvt[0]   = range[1];
    420:       delta[0] = uvt[0] - uvs[0];
    421:     }
    422:     if (uvt[1] < range[2]) {
    423:       uvt[1]   = range[2];
    424:       delta[1] = uvt[1] - uvs[1];
    425:     }
    426:     if (uvt[1] > range[3]) {
    427:       uvt[1]   = range[3];
    428:       delta[1] = uvt[1] - uvs[1];
    429:     }

    431:     if (islite) PetscCall(EGlite_evaluate(obj, uvt, data));
    432:     else PetscCall(EG_evaluate(obj, uvt, data));

    434:     obj_tmp = (coords[v * dE + 0] - data[0]) * (coords[v * dE + 0] - data[0]) + (coords[v * dE + 1] - data[1]) * (coords[v * dE + 1] - data[1]) + (coords[v * dE + 2] - data[2]) * (coords[v * dE + 2] - data[2]);

    436:     /* If step is better, accept it and halve lambda (making it more Newton-like) */
    437:     if (obj_tmp < obj_old) {
    438:       obj_old = obj_tmp;
    439:       uvs[0]  = uvt[0];
    440:       uvs[1]  = uvt[1];
    441:       for (int jj = 0; jj < 18; ++jj) eval[jj] = data[jj];
    442:       lambda /= 2.0;
    443:       if (lambda < 1.0E-14) lambda = 1.0E-14;
    444:       if (obj_old < target) {
    445:         tolr = obj_old;
    446:         break;
    447:       }
    448:     } else {
    449:       /* Otherwise reject it and double lambda (making it more gradient-descent like) */
    450:       lambda *= 2.0;
    451:     }

    453:     if (sqrt(delta[0] * delta[0] + delta[1] * delta[1]) < target) {
    454:       tolr = obj_old;
    455:       break;
    456:     }

    458:     tolr = obj_old;

    460:     loopCntr += 1;
    461:     if (loopCntr > 100) break;
    462:   }
    463:   paramsV[v * 3 + 0] = uvs[0];
    464:   paramsV[v * 3 + 1] = uvs[1];
    465:   PetscFunctionReturn(PETSC_SUCCESS);
    466: }

    468: PetscErrorCode DMSnapToGeomModel_EGADS_Internal(DM dm, PetscInt p, ego model, PetscInt bodyID, PetscInt faceID, PetscInt edgeID, const PetscScalar mcoords[], PetscScalar gcoords[], PetscBool islite)
    469: {
    470:   /* PETSc Variables */
    471:   DM   cdm;
    472:   ego *bodies;
    473:   ego  geom, body, obj;
    474:   /* result has to hold derivatives, along with the value */
    475:   double       params[3], result[18], paramsV[16 * 3], range[4];
    476:   int          Nb, oclass, mtype, *senses, peri;
    477:   Vec          coordinatesLocal;
    478:   PetscScalar *coords = NULL;
    479:   PetscInt     Nv, v, Np = 0, pm;
    480:   PetscInt     dE, d;
    481:   PetscReal    pTolr = 1.0e-14;

    483:   PetscFunctionBeginHot;
    484:   PetscCall(DMGetCoordinateDM(dm, &cdm));
    485:   PetscCall(DMGetCoordinateDim(dm, &dE));
    486:   PetscCall(DMGetCoordinatesLocal(dm, &coordinatesLocal));

    488:   if (islite) PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
    489:   else PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));

    491:   PetscCheck(bodyID < Nb, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Body %" PetscInt_FMT " is not in [0, %" PetscInt_FMT ")", bodyID, Nb);
    492:   body = bodies[bodyID];

    494:   if (edgeID >= 0) {
    495:     if (islite) {
    496:       PetscCall(EGlite_objectBodyTopo(body, EDGE, edgeID, &obj));
    497:       Np = 1;
    498:     } else {
    499:       PetscCall(EG_objectBodyTopo(body, EDGE, edgeID, &obj));
    500:       Np = 1;
    501:     }
    502:   } else if (faceID >= 0) {
    503:     if (islite) {
    504:       PetscCall(EGlite_objectBodyTopo(body, FACE, faceID, &obj));
    505:       Np = 2;
    506:     } else {
    507:       PetscCall(EG_objectBodyTopo(body, FACE, faceID, &obj));
    508:       Np = 2;
    509:     }
    510:   } else {
    511:     for (d = 0; d < dE; ++d) gcoords[d] = mcoords[d];
    512:     PetscFunctionReturn(PETSC_SUCCESS);
    513:   }

    515:   /* Calculate parameters (t or u,v) for vertices */
    516:   PetscCall(DMPlexVecGetClosure(cdm, NULL, coordinatesLocal, p, &Nv, &coords));
    517:   Nv /= dE;
    518:   if (Nv == 1) {
    519:     PetscCall(DMPlexVecRestoreClosure(cdm, NULL, coordinatesLocal, p, &Nv, &coords));
    520:     for (d = 0; d < dE; ++d) gcoords[d] = mcoords[d];
    521:     PetscFunctionReturn(PETSC_SUCCESS);
    522:   }
    523:   PetscCheck(Nv <= 16, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot handle %" PetscInt_FMT " coordinates associated to point %" PetscInt_FMT, Nv, p);

    525:   /* Correct EGADS/EGADSlite 2pi bug when calculating nearest point on Periodic Surfaces */
    526:   if (islite) PetscCall(EGlite_getRange(obj, range, &peri));
    527:   else PetscCall(EG_getRange(obj, range, &peri));

    529:   for (v = 0; v < Nv; ++v) {
    530:     if (edgeID > 0) {
    531:       PetscCall(DMPlex_Geom_EDGE_XYZtoUV_Internal(coords, obj, range, v, dE, paramsV, islite));
    532:     } else {
    533:       PetscCall(DMPlex_Geom_FACE_XYZtoUV_Internal(coords, obj, range, v, dE, paramsV, islite));
    534:     }
    535:   }
    536:   PetscCall(DMPlexVecRestoreClosure(cdm, NULL, coordinatesLocal, p, &Nv, &coords));

    538:   /* Calculate parameters (t or u,v) for new vertex at edge midpoint */
    539:   for (pm = 0; pm < Np; ++pm) {
    540:     params[pm] = 0.;
    541:     for (v = 0; v < Nv; ++v) params[pm] += paramsV[v * 3 + pm];
    542:     params[pm] /= Nv;
    543:   }
    544:   PetscCheck((params[0] + pTolr >= range[0]) || (params[0] - pTolr <= range[1]), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Point %" PetscInt_FMT " had bad interpolation", p);
    545:   PetscCheck(Np < 2 || ((params[1] + pTolr >= range[2]) || (params[1] - pTolr <= range[3])), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Point %d had bad interpolation on v", p);

    547:   /* Put coordinates for new vertex in result[] */
    548:   if (islite) PetscCall(EGlite_evaluate(obj, params, result));
    549:   else PetscCall(EG_evaluate(obj, params, result));

    551:   for (d = 0; d < dE; ++d) gcoords[d] = result[d];
    552:   PetscFunctionReturn(PETSC_SUCCESS);
    553: }
    554: #endif

    556: PetscErrorCode DMSnapToGeomModel_EGADS(DM dm, PetscInt p, PetscInt dE, const PetscScalar mcoords[], PetscScalar gcoords[])
    557: {
    558:   PetscFunctionBeginHot;
    559: #ifdef PETSC_HAVE_EGADS
    560:   DMLabel        bodyLabel, faceLabel, edgeLabel;
    561:   PetscInt       bodyID, faceID, edgeID;
    562:   PetscContainer modelObj;
    563:   ego            model;
    564:   PetscBool      islite = PETSC_FALSE;

    566:   // FIXME: Change -dm_plex_refine_without_snap_to_geom to DM to shut off snapping
    567:   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
    568:   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
    569:   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
    570:   PetscCheck(bodyLabel && faceLabel && edgeLabel, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "EGADS meshes must have body, face, and edge labels defined");
    571:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
    572:   if (!modelObj) {
    573:     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
    574:     islite = PETSC_TRUE;
    575:   }
    576:   PetscCheck(modelObj, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "EGADS mesh missing model object");

    578:   PetscCall(PetscContainerGetPointer(modelObj, &model));
    579:   PetscCall(DMLabelGetValue(bodyLabel, p, &bodyID));
    580:   PetscCall(DMLabelGetValue(faceLabel, p, &faceID));
    581:   PetscCall(DMLabelGetValue(edgeLabel, p, &edgeID));
    582:   /* Allows for "Connective" Plex Edges present in models with multiple non-touching Entities */
    583:   if (bodyID < 0) {
    584:     for (PetscInt d = 0; d < dE; ++d) gcoords[d] = mcoords[d];
    585:     PetscFunctionReturn(PETSC_SUCCESS);
    586:   }
    587:   PetscCall(DMSnapToGeomModel_EGADS_Internal(dm, p, model, bodyID, faceID, edgeID, mcoords, gcoords, islite));
    588: #endif
    589:   PetscFunctionReturn(PETSC_SUCCESS);
    590: }

    592: #if defined(PETSC_HAVE_EGADS)
    593: PetscErrorCode DMPlexGeomPrintModel_Internal(ego model, PetscBool islite)
    594: {
    595:   /* PETSc Variables */
    596:   ego geom, *bodies, *nobjs, *mobjs, *lobjs, *shobjs, *fobjs, *eobjs;
    597:   int oclass, mtype, *senses, *shsenses, *fsenses, *lsenses, *esenses;
    598:   int Nb, b;

    600:   PetscFunctionBeginUser;
    601:   /* test bodyTopo functions */
    602:   if (islite) PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
    603:   else PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
    604:   PetscCall(PetscPrintf(PETSC_COMM_SELF, " Number of BODIES (nbodies): %" PetscInt_FMT " \n", Nb));

    606:   for (b = 0; b < Nb; ++b) {
    607:     ego body = bodies[b];
    608:     int id, sh, Nsh, f, Nf, l, Nl, e, Ne, v, Nv;

    610:     /* List Topology of Bodies */
    611:     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
    612:     PetscCall(PetscPrintf(PETSC_COMM_SELF, "   BODY %d TOPOLOGY SUMMARY \n", b));

    614:     /* Output Basic Model Topology */
    615:     if (islite) PetscCall(EGlite_getBodyTopos(body, NULL, SHELL, &Nsh, &shobjs));
    616:     else PetscCall(EG_getBodyTopos(body, NULL, SHELL, &Nsh, &shobjs));
    617:     PetscCall(PetscPrintf(PETSC_COMM_SELF, "      Number of SHELLS: %d \n", Nsh));

    619:     if (islite) PetscCall(EGlite_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
    620:     else PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
    621:     PetscCall(PetscPrintf(PETSC_COMM_SELF, "      Number of FACES: %d \n", Nf));

    623:     if (islite) PetscCall(EGlite_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
    624:     else PetscCall(EG_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
    625:     PetscCall(PetscPrintf(PETSC_COMM_SELF, "      Number of LOOPS: %d \n", Nl));

    627:     if (islite) PetscCall(EGlite_getBodyTopos(body, NULL, EDGE, &Ne, &eobjs));
    628:     else PetscCall(EG_getBodyTopos(body, NULL, EDGE, &Ne, &eobjs));
    629:     PetscCall(PetscPrintf(PETSC_COMM_SELF, "      Number of EDGES: %d \n", Ne));

    631:     if (islite) PetscCall(EGlite_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
    632:     else PetscCall(EG_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
    633:     PetscCall(PetscPrintf(PETSC_COMM_SELF, "      Number of NODES: %d \n", Nv));

    635:     if (islite) {
    636:       EGlite_free(shobjs);
    637:       EGlite_free(fobjs);
    638:       EGlite_free(lobjs);
    639:       EGlite_free(eobjs);
    640:       EGlite_free(nobjs);
    641:     } else {
    642:       EG_free(shobjs);
    643:       EG_free(fobjs);
    644:       EG_free(lobjs);
    645:       EG_free(eobjs);
    646:       EG_free(nobjs);
    647:     }

    649:     /* List Topology of Bodies */
    650:     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
    651:     PetscCall(PetscPrintf(PETSC_COMM_SELF, "      BODY %d TOPOLOGY DETAILS \n", b));

    653:     /* Get SHELL info which associated with the current BODY */
    654:     if (islite) PetscCall(EGlite_getTopology(body, &geom, &oclass, &mtype, NULL, &Nsh, &shobjs, &shsenses));
    655:     else PetscCall(EG_getTopology(body, &geom, &oclass, &mtype, NULL, &Nsh, &shobjs, &shsenses));

    657:     for (sh = 0; sh < Nsh; ++sh) {
    658:       ego shell   = shobjs[sh];
    659:       int shsense = shsenses[sh];

    661:       if (islite) id = EGlite_indexBodyTopo(body, shell);
    662:       else id = EG_indexBodyTopo(body, shell);
    663:       PetscCall(PetscPrintf(PETSC_COMM_SELF, "         SHELL ID: %d :: sense = %d\n", id, shsense));

    665:       /* Get FACE information associated with current SHELL */
    666:       if (islite) PetscCall(EGlite_getTopology(shell, &geom, &oclass, &mtype, NULL, &Nf, &fobjs, &fsenses));
    667:       else PetscCall(EG_getTopology(shell, &geom, &oclass, &mtype, NULL, &Nf, &fobjs, &fsenses));

    669:       for (f = 0; f < Nf; ++f) {
    670:         ego     face = fobjs[f];
    671:         ego     gRef, gPrev, gNext;
    672:         int     goclass, gmtype, *gpinfo;
    673:         double *gprv;
    674:         char   *gClass = (char *)"", *gType = (char *)"";
    675:         double  fdata[4];
    676:         ego     fRef, fPrev, fNext;
    677:         int     foclass, fmtype;

    679:         if (islite) id = EGlite_indexBodyTopo(body, face);
    680:         else id = EG_indexBodyTopo(body, face);

    682:         /* Get LOOP info associated with current FACE */
    683:         if (islite) {
    684:           PetscCall(EGlite_getTopology(face, &geom, &oclass, &mtype, fdata, &Nl, &lobjs, &lsenses));
    685:           PetscCall(EGlite_getInfo(face, &foclass, &fmtype, &fRef, &fPrev, &fNext));
    686:           PetscCall(EGlite_getGeometry(geom, &goclass, &gmtype, &gRef, &gpinfo, &gprv));
    687:           PetscCall(EGlite_getInfo(geom, &goclass, &gmtype, &gRef, &gPrev, &gNext));
    688:         } else {
    689:           PetscCall(EG_getTopology(face, &geom, &oclass, &mtype, fdata, &Nl, &lobjs, &lsenses));
    690:           PetscCall(EG_getInfo(face, &foclass, &fmtype, &fRef, &fPrev, &fNext));
    691:           PetscCall(EG_getGeometry(geom, &goclass, &gmtype, &gRef, &gpinfo, &gprv));
    692:           PetscCall(EG_getInfo(geom, &goclass, &gmtype, &gRef, &gPrev, &gNext));
    693:         }

    695:         PetscCall(DMPlex_EGADS_GeomDecode_Internal(goclass, gmtype, &gClass, &gType));
    696:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "           FACE ID: %d :: sense = %d \n", id, fmtype));
    697:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "             GEOMETRY CLASS: %s \n", gClass));
    698:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "             GEOMETRY TYPE:  %s \n\n", gType));
    699:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "             RANGE (umin, umax) = (%f, %f) \n", fdata[0], fdata[1]));
    700:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "                   (vmin, vmax) = (%f, %f) \n\n", fdata[2], fdata[3]));

    702:         for (l = 0; l < Nl; ++l) {
    703:           ego loop   = lobjs[l];
    704:           int lsense = lsenses[l];

    706:           if (islite) id = EGlite_indexBodyTopo(body, loop);
    707:           else id = EG_indexBodyTopo(body, loop);

    709:           PetscCall(PetscPrintf(PETSC_COMM_SELF, "             LOOP ID: %d :: sense = %d\n", id, lsense));

    711:           /* Get EDGE info associated with the current LOOP */
    712:           if (islite) PetscCall(EGlite_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &eobjs, &esenses));
    713:           else PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &eobjs, &esenses));

    715:           for (e = 0; e < Ne; ++e) {
    716:             ego    edge = eobjs[e];
    717:             ego    topRef, prev, next;
    718:             int    esense   = esenses[e];
    719:             double range[4] = {0., 0., 0., 0.};
    720:             int    peri;
    721:             ego    gEref, gEprev, gEnext;
    722:             int    gEoclass, gEmtype;
    723:             char  *gEclass = (char *)"", *gEtype = (char *)"";

    725:             if (islite) {
    726:               PetscCall(EGlite_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
    727:               if (mtype != DEGENERATE) PetscCall(EGlite_getInfo(geom, &gEoclass, &gEmtype, &gEref, &gEprev, &gEnext));
    728:             } else {
    729:               PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
    730:               PetscCall(EG_getInfo(geom, &gEoclass, &gEmtype, &gEref, &gEprev, &gEnext));
    731:             }

    733:             if (mtype != DEGENERATE) PetscCall(DMPlex_EGADS_GeomDecode_Internal(gEoclass, gEmtype, &gEclass, &gEtype));

    735:             if (islite) {
    736:               id = EGlite_indexBodyTopo(body, edge);
    737:               PetscCall(EGlite_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
    738:             } else {
    739:               id = EG_indexBodyTopo(body, edge);
    740:               PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
    741:             }

    743:             PetscCall(PetscPrintf(PETSC_COMM_SELF, "               EDGE ID: %d :: sense = %d\n", id, esense));
    744:             if (mtype != DEGENERATE) {
    745:               PetscCall(PetscPrintf(PETSC_COMM_SELF, "                 GEOMETRY CLASS: %s \n", gEclass));
    746:               PetscCall(PetscPrintf(PETSC_COMM_SELF, "                 GEOMETRY TYPE:  %s \n", gEtype));
    747:             }

    749:             if (mtype == DEGENERATE) PetscCall(PetscPrintf(PETSC_COMM_SELF, "                 EDGE %d is DEGENERATE \n", id));

    751:             if (islite) PetscCall(EGlite_getRange(edge, range, &peri));
    752:             else PetscCall(EG_getRange(edge, range, &peri));

    754:             PetscCall(PetscPrintf(PETSC_COMM_SELF, "                 Peri = %d :: Range = %lf, %lf, %lf, %lf \n", peri, range[0], range[1], range[2], range[3]));

    756:             /* Get NODE info associated with the current EDGE */
    757:             if (islite) PetscCall(EGlite_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
    758:             else PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));

    760:             for (v = 0; v < Nv; ++v) {
    761:               ego    vertex = nobjs[v];
    762:               double limits[4];
    763:               int    unused;

    765:               if (islite) {
    766:                 PetscCall(EGlite_getTopology(vertex, &geom, &oclass, &mtype, limits, &unused, &mobjs, &senses));
    767:                 id = EGlite_indexBodyTopo(body, vertex);
    768:               } else {
    769:                 PetscCall(EG_getTopology(vertex, &geom, &oclass, &mtype, limits, &unused, &mobjs, &senses));
    770:                 id = EG_indexBodyTopo(body, vertex);
    771:               }
    772:               PetscCall(PetscPrintf(PETSC_COMM_SELF, "                 NODE ID: %d \n", id));
    773:               PetscCall(PetscPrintf(PETSC_COMM_SELF, "                    (x, y, z) = (%lf, %lf, %lf) \n", limits[0], limits[1], limits[2]));
    774:             }
    775:           }
    776:         }
    777:       }
    778:     }
    779:   }
    780:   PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n\n"));
    781:   PetscFunctionReturn(PETSC_SUCCESS);
    782: }

    784: static PetscErrorCode DMPlexEGADSDestroy_Private(PetscCtxRt context)
    785: {
    786:   if (*(void **)context) EG_deleteObject((ego) * (void **)context);
    787:   return PETSC_SUCCESS;
    788: }

    790: static PetscErrorCode DMPlexEGADSClose_Private(PetscCtxRt context)
    791: {
    792:   if (*(void **)context) EG_close((ego) * (void **)context);
    793:   return PETSC_SUCCESS;
    794: }

    796: PetscErrorCode DMPlexEGADSliteDestroy_Private(PetscCtxRt context)
    797: {
    798:   if (*(void **)context) EGlite_deleteObject((ego) * (void **)context);
    799:   return PETSC_SUCCESS;
    800: }

    802: PetscErrorCode DMPlexEGADSliteClose_Private(PetscCtxRt context)
    803: {
    804:   if (*(void **)context) EGlite_close((ego) * (void **)context);
    805:   return PETSC_SUCCESS;
    806: }

    808: PetscErrorCode DMPlexCreateGeom_Internal(MPI_Comm comm, ego context, ego model, DM *newdm, PetscBool islite)
    809: {
    810:   /* EGADS variables */
    811:   ego geom, *bodies, *objs, *nobjs, *mobjs, *lobjs;
    812:   int oclass, mtype, nbodies, *senses;
    813:   int b;
    814:   /* PETSc variables */
    815:   DM          dm;
    816:   DMLabel     bodyLabel, faceLabel, edgeLabel, vertexLabel;
    817:   PetscHMapI  edgeMap = NULL;
    818:   PetscInt    cStart, cEnd, c;
    819:   PetscInt    dim = -1, cdim = -1, numCorners = 0, maxCorners = 0, numVertices = 0, newVertices = 0, numEdges = 0, numCells = 0, newCells = 0, numQuads = 0, cOff = 0, fOff = 0;
    820:   PetscInt   *cells = NULL, *cone = NULL;
    821:   PetscReal  *coords = NULL;
    822:   PetscMPIInt rank;

    824:   PetscFunctionBegin;
    825:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
    826:   if (rank == 0) {
    827:     const PetscInt debug = 0;

    829:     /*
    830:       Generate PETSc DMPlex
    831:       Get all Nodes in model, record coordinates in a correctly formatted array
    832:       Cycle through bodies, cycle through loops, recorde NODE IDs in a correctly formatted array
    833:       We need to uniformly refine the initial geometry to guarantee a valid mesh
    834:     */

    836:     /* Calculate cell and vertex sizes */
    837:     if (islite) PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, &nbodies, &bodies, &senses));
    838:     else PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &nbodies, &bodies, &senses));

    840:     PetscCall(PetscHMapICreate(&edgeMap));
    841:     numEdges = 0;
    842:     for (b = 0; b < nbodies; ++b) {
    843:       ego body = bodies[b];
    844:       int id, Nl, l, Nv, v;

    846:       if (islite) PetscCall(EGlite_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
    847:       else PetscCall(EG_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));

    849:       for (l = 0; l < Nl; ++l) {
    850:         ego loop = lobjs[l];
    851:         int Ner  = 0, Ne, e, Nc;

    853:         if (islite) PetscCall(EGlite_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));
    854:         else PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));

    856:         for (e = 0; e < Ne; ++e) {
    857:           ego           edge = objs[e];
    858:           int           Nv, id;
    859:           PetscHashIter iter;
    860:           PetscBool     found;

    862:           if (islite) PetscCall(EGlite_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
    863:           else PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));

    865:           if (mtype == DEGENERATE) continue;

    867:           if (islite) {
    868:             id = EGlite_indexBodyTopo(body, edge);
    869:           } else {
    870:             id = EG_indexBodyTopo(body, edge);
    871:           }

    873:           PetscCall(PetscHMapIFind(edgeMap, id - 1, &iter, &found));
    874:           if (!found) PetscCall(PetscHMapISet(edgeMap, id - 1, numEdges++));
    875:           ++Ner;
    876:         }
    877:         if (Ner == 2) {
    878:           Nc = 2;
    879:         } else if (Ner == 3) {
    880:           Nc = 4;
    881:         } else if (Ner == 4) {
    882:           Nc = 8;
    883:           ++numQuads;
    884:         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot support loop with %d edges", Ner);
    885:         numCells += Nc;
    886:         newCells += Nc - 1;
    887:         maxCorners = PetscMax(Ner * 2 + 1, maxCorners);
    888:       }
    889:       if (islite) PetscCall(EGlite_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
    890:       else PetscCall(EG_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));

    892:       for (v = 0; v < Nv; ++v) {
    893:         ego vertex = nobjs[v];

    895:         if (islite) {
    896:           id = EGlite_indexBodyTopo(body, vertex);
    897:         } else {
    898:           id = EG_indexBodyTopo(body, vertex);
    899:         }
    900:         /* TODO: Instead of assuming contiguous ids, we could use a hash table */
    901:         numVertices = PetscMax(id, numVertices);
    902:       }
    903:       if (islite) {
    904:         EGlite_free(lobjs);
    905:         EGlite_free(nobjs);
    906:       } else {
    907:         EG_free(lobjs);
    908:         EG_free(nobjs);
    909:       }
    910:     }
    911:     PetscCall(PetscHMapIGetSize(edgeMap, &numEdges));
    912:     newVertices = numEdges + numQuads;
    913:     numVertices += newVertices;

    915:     dim        = 2; /* Assume 3D Models :: Need to update to handle 2D Models in the future */
    916:     cdim       = 3; /* Assume 3D Models :: Need to update to handle 2D Models in the future */
    917:     numCorners = 3; /* Split cells into triangles */
    918:     PetscCall(PetscMalloc3(numVertices * cdim, &coords, numCells * numCorners, &cells, maxCorners, &cone));

    920:     /* Get vertex coordinates */
    921:     for (b = 0; b < nbodies; ++b) {
    922:       ego body = bodies[b];
    923:       int id, Nv, v;

    925:       if (islite) PetscCall(EGlite_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
    926:       else PetscCall(EG_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));

    928:       for (v = 0; v < Nv; ++v) {
    929:         ego    vertex = nobjs[v];
    930:         double limits[4];
    931:         int    unused;

    933:         if (islite) {
    934:           PetscCall(EGlite_getTopology(vertex, &geom, &oclass, &mtype, limits, &unused, &mobjs, &senses));
    935:           id = EGlite_indexBodyTopo(body, vertex);
    936:         } else {
    937:           PetscCall(EG_getTopology(vertex, &geom, &oclass, &mtype, limits, &unused, &mobjs, &senses));
    938:           id = EG_indexBodyTopo(body, vertex);
    939:         }

    941:         coords[(id - 1) * cdim + 0] = limits[0];
    942:         coords[(id - 1) * cdim + 1] = limits[1];
    943:         coords[(id - 1) * cdim + 2] = limits[2];
    944:       }
    945:       if (islite) {
    946:         EGlite_free(nobjs);
    947:       } else {
    948:         EG_free(nobjs);
    949:       }
    950:     }
    951:     PetscCall(PetscHMapIClear(edgeMap));
    952:     fOff     = numVertices - newVertices + numEdges;
    953:     numEdges = 0;
    954:     numQuads = 0;
    955:     for (b = 0; b < nbodies; ++b) {
    956:       ego body = bodies[b];
    957:       int Nl, l;

    959:       if (islite) PetscCall(EGlite_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
    960:       else PetscCall(EG_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));

    962:       for (l = 0; l < Nl; ++l) {
    963:         ego loop = lobjs[l];
    964:         int lid, Ner = 0, Ne, e;

    966:         if (islite) {
    967:           lid = EGlite_indexBodyTopo(body, loop);
    968:           PetscCall(EGlite_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));
    969:         } else {
    970:           lid = EG_indexBodyTopo(body, loop);
    971:           PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));
    972:         }

    974:         for (e = 0; e < Ne; ++e) {
    975:           ego           edge = objs[e];
    976:           int           eid, Nv;
    977:           PetscHashIter iter;
    978:           PetscBool     found;

    980:           if (islite) PetscCall(EGlite_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
    981:           else PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));

    983:           if (mtype == DEGENERATE) continue;
    984:           ++Ner;

    986:           if (islite) {
    987:             eid = EGlite_indexBodyTopo(body, edge);
    988:           } else {
    989:             eid = EG_indexBodyTopo(body, edge);
    990:           }

    992:           PetscCall(PetscHMapIFind(edgeMap, eid - 1, &iter, &found));
    993:           if (!found) {
    994:             PetscInt v = numVertices - newVertices + numEdges;
    995:             double   range[4], params[3] = {0., 0., 0.}, result[18];
    996:             int      periodic[2];

    998:             PetscCall(PetscHMapISet(edgeMap, eid - 1, numEdges++));

    1000:             if (islite) {
    1001:               PetscCall(EGlite_getRange(edge, range, periodic));
    1002:             } else {
    1003:               PetscCall(EG_getRange(edge, range, periodic));
    1004:             }

    1006:             params[0] = 0.5 * (range[0] + range[1]);
    1007:             if (islite) {
    1008:               PetscCall(EGlite_evaluate(edge, params, result));
    1009:             } else {
    1010:               PetscCall(EG_evaluate(edge, params, result));
    1011:             }
    1012:             coords[v * cdim + 0] = result[0];
    1013:             coords[v * cdim + 1] = result[1];
    1014:             coords[v * cdim + 2] = result[2];
    1015:           }
    1016:         }
    1017:         if (Ner == 4) {
    1018:           PetscInt v = fOff + numQuads++;
    1019:           ego     *fobjs, face;
    1020:           double   range[4], params[3] = {0., 0., 0.}, result[18];
    1021:           int      Nf, fid, periodic[2];

    1023:           if (islite) PetscCall(EGlite_getBodyTopos(body, loop, FACE, &Nf, &fobjs));
    1024:           else PetscCall(EG_getBodyTopos(body, loop, FACE, &Nf, &fobjs));
    1025:           face = fobjs[0];

    1027:           if (islite) {
    1028:             fid = EGlite_indexBodyTopo(body, face);
    1029:           } else {
    1030:             fid = EG_indexBodyTopo(body, face);
    1031:           }

    1033:           PetscCheck(Nf != 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Loop %d has %" PetscInt_FMT " faces, instead of 1 (%" PetscInt_FMT ")", lid - 1, Nf, fid);
    1034:           if (islite) {
    1035:             PetscCall(EGlite_getRange(face, range, periodic));
    1036:           } else {
    1037:             PetscCall(EG_getRange(face, range, periodic));
    1038:           }
    1039:           params[0] = 0.5 * (range[0] + range[1]);
    1040:           params[1] = 0.5 * (range[2] + range[3]);
    1041:           if (islite) {
    1042:             PetscCall(EGlite_evaluate(face, params, result));
    1043:           } else {
    1044:             PetscCall(EG_evaluate(face, params, result));
    1045:           }
    1046:           coords[v * cdim + 0] = result[0];
    1047:           coords[v * cdim + 1] = result[1];
    1048:           coords[v * cdim + 2] = result[2];
    1049:         }
    1050:       }
    1051:     }
    1052:     PetscCheck(numEdges + numQuads == newVertices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of new vertices %d != %d previous count", numEdges + numQuads, newVertices);

    1054:     /* Get cell vertices by traversing loops */
    1055:     numQuads = 0;
    1056:     cOff     = 0;
    1057:     for (b = 0; b < nbodies; ++b) {
    1058:       ego body = bodies[b];
    1059:       int id, Nl, l;

    1061:       if (islite) PetscCall(EGlite_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
    1062:       else PetscCall(EG_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
    1063:       for (l = 0; l < Nl; ++l) {
    1064:         ego loop = lobjs[l];
    1065:         int lid, Ner = 0, Ne, e, nc = 0, c, Nt, t;

    1067:         if (islite) {
    1068:           lid = EGlite_indexBodyTopo(body, loop);
    1069:           PetscCall(EGlite_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));
    1070:         } else {
    1071:           lid = EG_indexBodyTopo(body, loop);
    1072:           PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));
    1073:         }

    1075:         for (e = 0; e < Ne; ++e) {
    1076:           ego edge = objs[e];
    1077:           int points[3];
    1078:           int eid, Nv, v, tmp;

    1080:           if (islite) {
    1081:             eid = EGlite_indexBodyTopo(body, edge);
    1082:             PetscCall(EGlite_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
    1083:           } else {
    1084:             eid = EG_indexBodyTopo(body, edge);
    1085:             PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
    1086:           }

    1088:           if (mtype == DEGENERATE) continue;
    1089:           else ++Ner;
    1090:           PetscCheck(Nv == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Edge %" PetscInt_FMT " has %" PetscInt_FMT " vertices != 2", eid, Nv);

    1092:           for (v = 0; v < Nv; ++v) {
    1093:             ego vertex = nobjs[v];

    1095:             if (islite) {
    1096:               id = EGlite_indexBodyTopo(body, vertex);
    1097:             } else {
    1098:               id = EG_indexBodyTopo(body, vertex);
    1099:             }
    1100:             points[v * 2] = id - 1;
    1101:           }
    1102:           {
    1103:             PetscInt edgeNum;

    1105:             PetscCall(PetscHMapIGet(edgeMap, eid - 1, &edgeNum));
    1106:             points[1] = numVertices - newVertices + edgeNum;
    1107:           }
    1108:           /* EGADS loops are not oriented, but seem to be in order, so we must piece them together */
    1109:           if (!nc) {
    1110:             for (v = 0; v < Nv + 1; ++v) cone[nc++] = points[v];
    1111:           } else {
    1112:             if (cone[nc - 1] == points[0]) {
    1113:               cone[nc++] = points[1];
    1114:               if (cone[0] != points[2]) cone[nc++] = points[2];
    1115:             } else if (cone[nc - 1] == points[2]) {
    1116:               cone[nc++] = points[1];
    1117:               if (cone[0] != points[0]) cone[nc++] = points[0];
    1118:             } else if (cone[nc - 3] == points[0]) {
    1119:               tmp          = cone[nc - 3];
    1120:               cone[nc - 3] = cone[nc - 1];
    1121:               cone[nc - 1] = tmp;
    1122:               cone[nc++]   = points[1];
    1123:               if (cone[0] != points[2]) cone[nc++] = points[2];
    1124:             } else if (cone[nc - 3] == points[2]) {
    1125:               tmp          = cone[nc - 3];
    1126:               cone[nc - 3] = cone[nc - 1];
    1127:               cone[nc - 1] = tmp;
    1128:               cone[nc++]   = points[1];
    1129:               if (cone[0] != points[0]) cone[nc++] = points[0];
    1130:             } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Edge %d does not match its predecessor", eid);
    1131:           }
    1132:         }
    1133:         PetscCheck(nc == 2 * Ner, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Number of corners %" PetscInt_FMT " != %" PetscInt_FMT, nc, 2 * Ner);
    1134:         if (Ner == 4) cone[nc++] = numVertices - newVertices + numEdges + numQuads++;
    1135:         PetscCheck(nc <= maxCorners, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Number of corners %" PetscInt_FMT " > %" PetscInt_FMT " max", nc, maxCorners);
    1136:         /* Triangulate the loop */
    1137:         switch (Ner) {
    1138:         case 2: /* Bi-Segment -> 2 triangles */
    1139:           Nt                           = 2;
    1140:           cells[cOff * numCorners + 0] = cone[0];
    1141:           cells[cOff * numCorners + 1] = cone[1];
    1142:           cells[cOff * numCorners + 2] = cone[2];
    1143:           ++cOff;
    1144:           cells[cOff * numCorners + 0] = cone[0];
    1145:           cells[cOff * numCorners + 1] = cone[2];
    1146:           cells[cOff * numCorners + 2] = cone[3];
    1147:           ++cOff;
    1148:           break;
    1149:         case 3: /* Triangle   -> 4 triangles */
    1150:           Nt                           = 4;
    1151:           cells[cOff * numCorners + 0] = cone[0];
    1152:           cells[cOff * numCorners + 1] = cone[1];
    1153:           cells[cOff * numCorners + 2] = cone[5];
    1154:           ++cOff;
    1155:           cells[cOff * numCorners + 0] = cone[1];
    1156:           cells[cOff * numCorners + 1] = cone[2];
    1157:           cells[cOff * numCorners + 2] = cone[3];
    1158:           ++cOff;
    1159:           cells[cOff * numCorners + 0] = cone[5];
    1160:           cells[cOff * numCorners + 1] = cone[3];
    1161:           cells[cOff * numCorners + 2] = cone[4];
    1162:           ++cOff;
    1163:           cells[cOff * numCorners + 0] = cone[1];
    1164:           cells[cOff * numCorners + 1] = cone[3];
    1165:           cells[cOff * numCorners + 2] = cone[5];
    1166:           ++cOff;
    1167:           break;
    1168:         case 4: /* Quad       -> 8 triangles */
    1169:           Nt                           = 8;
    1170:           cells[cOff * numCorners + 0] = cone[0];
    1171:           cells[cOff * numCorners + 1] = cone[1];
    1172:           cells[cOff * numCorners + 2] = cone[7];
    1173:           ++cOff;
    1174:           cells[cOff * numCorners + 0] = cone[1];
    1175:           cells[cOff * numCorners + 1] = cone[2];
    1176:           cells[cOff * numCorners + 2] = cone[3];
    1177:           ++cOff;
    1178:           cells[cOff * numCorners + 0] = cone[3];
    1179:           cells[cOff * numCorners + 1] = cone[4];
    1180:           cells[cOff * numCorners + 2] = cone[5];
    1181:           ++cOff;
    1182:           cells[cOff * numCorners + 0] = cone[5];
    1183:           cells[cOff * numCorners + 1] = cone[6];
    1184:           cells[cOff * numCorners + 2] = cone[7];
    1185:           ++cOff;
    1186:           cells[cOff * numCorners + 0] = cone[8];
    1187:           cells[cOff * numCorners + 1] = cone[1];
    1188:           cells[cOff * numCorners + 2] = cone[3];
    1189:           ++cOff;
    1190:           cells[cOff * numCorners + 0] = cone[8];
    1191:           cells[cOff * numCorners + 1] = cone[3];
    1192:           cells[cOff * numCorners + 2] = cone[5];
    1193:           ++cOff;
    1194:           cells[cOff * numCorners + 0] = cone[8];
    1195:           cells[cOff * numCorners + 1] = cone[5];
    1196:           cells[cOff * numCorners + 2] = cone[7];
    1197:           ++cOff;
    1198:           cells[cOff * numCorners + 0] = cone[8];
    1199:           cells[cOff * numCorners + 1] = cone[7];
    1200:           cells[cOff * numCorners + 2] = cone[1];
    1201:           ++cOff;
    1202:           break;
    1203:         default:
    1204:           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Loop %d has %d edges, which we do not support", lid, Ner);
    1205:         }
    1206:         if (debug) {
    1207:           for (t = 0; t < Nt; ++t) {
    1208:             PetscCall(PetscPrintf(PETSC_COMM_SELF, "  LOOP Corner NODEs Triangle %d (", t));
    1209:             for (c = 0; c < numCorners; ++c) {
    1210:               if (c > 0) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
    1211:               PetscCall(PetscPrintf(PETSC_COMM_SELF, "%d", cells[(cOff - Nt + t) * numCorners + c]));
    1212:             }
    1213:             PetscCall(PetscPrintf(PETSC_COMM_SELF, ")\n"));
    1214:           }
    1215:         }
    1216:       }
    1217:       if (islite) {
    1218:         EGlite_free(lobjs);
    1219:       } else {
    1220:         EG_free(lobjs);
    1221:       }
    1222:     }
    1223:   }
    1224:   PetscCheck(cOff == numCells, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Count of total cells %d != %d previous count", cOff, numCells);
    1225:   PetscCall(DMPlexCreateFromCellListPetsc(PETSC_COMM_WORLD, dim, numCells, numVertices, numCorners, PETSC_TRUE, cells, cdim, coords, &dm));
    1226:   PetscCall(PetscFree3(coords, cells, cone));
    1227:   PetscCall(PetscInfo(dm, " Total Number of Unique Cells    = %d (%d)\n", numCells, newCells));
    1228:   PetscCall(PetscInfo(dm, " Total Number of Unique Vertices = %d (%d)\n", numVertices, newVertices));
    1229:   /* Embed EGADS model in DM */
    1230:   {
    1231:     PetscContainer modelObj, contextObj;

    1233:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &modelObj));
    1234:     PetscCall(PetscContainerSetPointer(modelObj, model));
    1235:     PetscCall(PetscContainerSetCtxDestroy(modelObj, (PetscCtxDestroyFn *)DMPlexEGADSDestroy_Private));
    1236:     PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Model", (PetscObject)modelObj));
    1237:     PetscCall(PetscContainerDestroy(&modelObj));

    1239:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &contextObj));
    1240:     PetscCall(PetscContainerSetPointer(contextObj, context));
    1241:     PetscCall(PetscContainerSetCtxDestroy(contextObj, (PetscCtxDestroyFn *)DMPlexEGADSClose_Private));
    1242:     PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Context", (PetscObject)contextObj));
    1243:     PetscCall(PetscContainerDestroy(&contextObj));
    1244:   }
    1245:   /* Label points */
    1246:   PetscCall(DMCreateLabel(dm, "EGADS Body ID"));
    1247:   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
    1248:   PetscCall(DMCreateLabel(dm, "EGADS Face ID"));
    1249:   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
    1250:   PetscCall(DMCreateLabel(dm, "EGADS Edge ID"));
    1251:   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
    1252:   PetscCall(DMCreateLabel(dm, "EGADS Vertex ID"));
    1253:   PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));
    1254:   cOff = 0;
    1255:   for (b = 0; b < nbodies; ++b) {
    1256:     ego body = bodies[b];
    1257:     int id, Nl, l;

    1259:     if (islite) PetscCall(EGlite_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
    1260:     else PetscCall(EG_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
    1261:     for (l = 0; l < Nl; ++l) {
    1262:       ego  loop = lobjs[l];
    1263:       ego *fobjs;
    1264:       int  lid, Nf, fid, Ner = 0, Ne, e, Nt = 0, t;

    1266:       if (islite) {
    1267:         lid = EGlite_indexBodyTopo(body, loop);
    1268:         PetscCall(EGlite_getBodyTopos(body, loop, FACE, &Nf, &fobjs));
    1269:       } else {
    1270:         lid = EG_indexBodyTopo(body, loop);
    1271:         PetscCall(EG_getBodyTopos(body, loop, FACE, &Nf, &fobjs));
    1272:       }

    1274:       PetscCheck(Nf <= 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Loop %d has %d > 1 faces, which is not supported", lid, Nf);
    1275:       if (islite) {
    1276:         fid = EGlite_indexBodyTopo(body, fobjs[0]);
    1277:         EGlite_free(fobjs);
    1278:         PetscCall(EGlite_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));
    1279:       } else {
    1280:         fid = EG_indexBodyTopo(body, fobjs[0]);
    1281:         EG_free(fobjs);
    1282:         PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));
    1283:       }

    1285:       for (e = 0; e < Ne; ++e) {
    1286:         ego             edge = objs[e];
    1287:         int             eid, Nv, v;
    1288:         PetscInt        points[3], support[2], numEdges, edgeNum;
    1289:         const PetscInt *edges;

    1291:         if (islite) {
    1292:           eid = EGlite_indexBodyTopo(body, edge);
    1293:           PetscCall(EGlite_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
    1294:         } else {
    1295:           eid = EG_indexBodyTopo(body, edge);
    1296:           PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
    1297:         }

    1299:         if (mtype == DEGENERATE) continue;
    1300:         else ++Ner;
    1301:         for (v = 0; v < Nv; ++v) {
    1302:           ego vertex = nobjs[v];

    1304:           if (islite) {
    1305:             id = EGlite_indexBodyTopo(body, vertex);
    1306:           } else {
    1307:             id = EG_indexBodyTopo(body, vertex);
    1308:           }

    1310:           PetscCall(DMLabelSetValue(edgeLabel, numCells + id - 1, eid));
    1311:           points[v * 2] = numCells + id - 1;
    1312:         }
    1313:         PetscCall(PetscHMapIGet(edgeMap, eid - 1, &edgeNum));
    1314:         points[1] = numCells + numVertices - newVertices + edgeNum;

    1316:         PetscCall(DMLabelSetValue(edgeLabel, points[1], eid));
    1317:         support[0] = points[0];
    1318:         support[1] = points[1];
    1319:         PetscCall(DMPlexGetJoin(dm, 2, support, &numEdges, &edges));
    1320:         PetscCheck(numEdges == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Vertices (%d, %d) should only bound 1 edge, not %d", support[0], support[1], numEdges);
    1321:         PetscCall(DMLabelSetValue(edgeLabel, edges[0], eid));
    1322:         PetscCall(DMPlexRestoreJoin(dm, 2, support, &numEdges, &edges));
    1323:         support[0] = points[1];
    1324:         support[1] = points[2];
    1325:         PetscCall(DMPlexGetJoin(dm, 2, support, &numEdges, &edges));
    1326:         PetscCheck(numEdges == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Vertices (%d, %d) should only bound 1 edge, not %d", support[0], support[1], numEdges);
    1327:         PetscCall(DMLabelSetValue(edgeLabel, edges[0], eid));
    1328:         PetscCall(DMPlexRestoreJoin(dm, 2, support, &numEdges, &edges));
    1329:       }
    1330:       switch (Ner) {
    1331:       case 2:
    1332:         Nt = 2;
    1333:         break;
    1334:       case 3:
    1335:         Nt = 4;
    1336:         break;
    1337:       case 4:
    1338:         Nt = 8;
    1339:         break;
    1340:       default:
    1341:         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Loop with %d edges is unsupported", Ner);
    1342:       }
    1343:       for (t = 0; t < Nt; ++t) {
    1344:         PetscCall(DMLabelSetValue(bodyLabel, cOff + t, b));
    1345:         PetscCall(DMLabelSetValue(faceLabel, cOff + t, fid));
    1346:       }
    1347:       cOff += Nt;
    1348:     }
    1349:     if (islite) {
    1350:       EGlite_free(lobjs);
    1351:     } else {
    1352:       EG_free(lobjs);
    1353:     }
    1354:   }
    1355:   PetscCall(PetscHMapIDestroy(&edgeMap));
    1356:   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
    1357:   for (c = cStart; c < cEnd; ++c) {
    1358:     PetscInt *closure = NULL;
    1359:     PetscInt  clSize, cl, bval, fval;

    1361:     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
    1362:     PetscCall(DMLabelGetValue(bodyLabel, c, &bval));
    1363:     PetscCall(DMLabelGetValue(faceLabel, c, &fval));
    1364:     for (cl = 0; cl < clSize * 2; cl += 2) {
    1365:       PetscCall(DMLabelSetValue(bodyLabel, closure[cl], bval));
    1366:       PetscCall(DMLabelSetValue(faceLabel, closure[cl], fval));
    1367:     }
    1368:     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
    1369:   }
    1370:   *newdm = dm;
    1371:   PetscFunctionReturn(PETSC_SUCCESS);
    1372: }

    1374: PetscErrorCode DMPlexCreateGeom(MPI_Comm comm, ego context, ego model, DM *newdm, PetscBool islite)
    1375: {
    1376:   // EGADS variables
    1377:   ego geom, *bodies, *mobjs, *fobjs, *lobjs, *eobjs, *nobjs;
    1378:   ego topRef, prev, next;
    1379:   int oclass, mtype, nbodies, *senses, *lSenses, *eSenses;
    1380:   int b;
    1381:   // PETSc variables
    1382:   DM              dm;
    1383:   DMLabel         bodyLabel, faceLabel, edgeLabel, vertexLabel;
    1384:   PetscHMapI      edgeMap = NULL, bodyIndexMap = NULL, bodyVertexMap = NULL, bodyEdgeMap = NULL, bodyFaceMap = NULL, bodyEdgeGlobalMap = NULL;
    1385:   PetscInt        dim = -1, cdim = -1, numCorners = 0, numVertices = 0, numEdges = 0, numFaces = 0, numCells = 0, edgeCntr = 0;
    1386:   PetscInt        cellCntr = 0, numPoints = 0;
    1387:   PetscInt       *cells  = NULL;
    1388:   const PetscInt *cone   = NULL;
    1389:   PetscReal      *coords = NULL;
    1390:   PetscMPIInt     rank;

    1392:   PetscFunctionBeginUser;
    1393:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
    1394:   if (rank == 0) {
    1395:     // Generate PETSc DMPlex
    1396:     //  Get all Nodes in model, record coordinates in a correctly formatted array
    1397:     //  Cycle through bodies, cycle through loops, recorde NODE IDs in a correctly formatted array
    1398:     //  We need to uniformly refine the initial geometry to guarantee a valid mesh

    1400:     // Calculate cell and vertex sizes
    1401:     if (islite) PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, &nbodies, &bodies, &senses));
    1402:     else PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &nbodies, &bodies, &senses));
    1403:     PetscCall(PetscHMapICreate(&edgeMap));
    1404:     PetscCall(PetscHMapICreate(&bodyIndexMap));
    1405:     PetscCall(PetscHMapICreate(&bodyVertexMap));
    1406:     PetscCall(PetscHMapICreate(&bodyEdgeMap));
    1407:     PetscCall(PetscHMapICreate(&bodyEdgeGlobalMap));
    1408:     PetscCall(PetscHMapICreate(&bodyFaceMap));

    1410:     for (b = 0; b < nbodies; ++b) {
    1411:       ego           body = bodies[b];
    1412:       int           Nf, Ne, Nv;
    1413:       PetscHashIter BIiter, BViter, BEiter, BEGiter, BFiter, EMiter;
    1414:       PetscBool     BIfound, BVfound, BEfound, BEGfound, BFfound, EMfound;

    1416:       PetscCall(PetscHMapIFind(bodyIndexMap, b, &BIiter, &BIfound));
    1417:       PetscCall(PetscHMapIFind(bodyVertexMap, b, &BViter, &BVfound));
    1418:       PetscCall(PetscHMapIFind(bodyEdgeMap, b, &BEiter, &BEfound));
    1419:       PetscCall(PetscHMapIFind(bodyEdgeGlobalMap, b, &BEGiter, &BEGfound));
    1420:       PetscCall(PetscHMapIFind(bodyFaceMap, b, &BFiter, &BFfound));

    1422:       if (!BIfound) PetscCall(PetscHMapISet(bodyIndexMap, b, numFaces + numEdges + numVertices));
    1423:       if (!BVfound) PetscCall(PetscHMapISet(bodyVertexMap, b, numVertices));
    1424:       if (!BEfound) PetscCall(PetscHMapISet(bodyEdgeMap, b, numEdges));
    1425:       if (!BEGfound) PetscCall(PetscHMapISet(bodyEdgeGlobalMap, b, edgeCntr));
    1426:       if (!BFfound) PetscCall(PetscHMapISet(bodyFaceMap, b, numFaces));

    1428:       if (islite) {
    1429:         PetscCall(EGlite_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
    1430:         PetscCall(EGlite_getBodyTopos(body, NULL, EDGE, &Ne, &eobjs));
    1431:         PetscCall(EGlite_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
    1432:         EGlite_free(fobjs);
    1433:         EGlite_free(eobjs);
    1434:         EGlite_free(nobjs);
    1435:       } else {
    1436:         PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
    1437:         PetscCall(EG_getBodyTopos(body, NULL, EDGE, &Ne, &eobjs));
    1438:         PetscCall(EG_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
    1439:         EG_free(fobjs);
    1440:         EG_free(eobjs);
    1441:         EG_free(nobjs);
    1442:       }

    1444:       // Remove DEGENERATE EDGES from Edge count
    1445:       if (islite) PetscCall(EGlite_getBodyTopos(body, NULL, EDGE, &Ne, &eobjs));
    1446:       else PetscCall(EG_getBodyTopos(body, NULL, EDGE, &Ne, &eobjs));

    1448:       int Netemp = 0;
    1449:       for (int e = 0; e < Ne; ++e) {
    1450:         ego edge = eobjs[e];
    1451:         int eid;

    1453:         if (islite) {
    1454:           PetscCall(EGlite_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
    1455:           eid = EGlite_indexBodyTopo(body, edge);
    1456:         } else {
    1457:           PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
    1458:           eid = EG_indexBodyTopo(body, edge);
    1459:         }

    1461:         PetscCall(PetscHMapIFind(edgeMap, edgeCntr + eid - 1, &EMiter, &EMfound));
    1462:         if (mtype == DEGENERATE) {
    1463:           if (!EMfound) PetscCall(PetscHMapISet(edgeMap, edgeCntr + eid - 1, -1));
    1464:         } else {
    1465:           ++Netemp;
    1466:           if (!EMfound) PetscCall(PetscHMapISet(edgeMap, edgeCntr + eid - 1, Netemp));
    1467:         }
    1468:       }
    1469:       if (islite) {
    1470:         EGlite_free(eobjs);
    1471:       } else {
    1472:         EG_free(eobjs);
    1473:       }

    1475:       // Determine Number of Cells
    1476:       if (islite) PetscCall(EGlite_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
    1477:       else PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));

    1479:       for (int f = 0; f < Nf; ++f) {
    1480:         ego face     = fobjs[f];
    1481:         int edgeTemp = 0;

    1483:         if (islite) PetscCall(EGlite_getBodyTopos(body, face, EDGE, &Ne, &eobjs));
    1484:         else PetscCall(EG_getBodyTopos(body, face, EDGE, &Ne, &eobjs));

    1486:         for (int e = 0; e < Ne; ++e) {
    1487:           ego edge = eobjs[e];

    1489:           if (islite) {
    1490:             PetscCall(EGlite_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
    1491:           } else {
    1492:             PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
    1493:           }
    1494:           if (mtype != DEGENERATE) ++edgeTemp;
    1495:         }
    1496:         numCells += (2 * edgeTemp);
    1497:         if (islite) {
    1498:           EGlite_free(eobjs);
    1499:         } else {
    1500:           EG_free(eobjs);
    1501:         }
    1502:       }
    1503:       if (islite) {
    1504:         EGlite_free(fobjs);
    1505:       } else {
    1506:         EG_free(fobjs);
    1507:       }

    1509:       numFaces += Nf;
    1510:       numEdges += Netemp;
    1511:       numVertices += Nv;
    1512:       edgeCntr += Ne;
    1513:     }

    1515:     // Set up basic DMPlex parameters
    1516:     dim        = 2;                                 // Assumes 3D Models :: Need to handle 2D models in the future
    1517:     cdim       = 3;                                 // Assumes 3D Models :: Need to update to handle 2D models in future
    1518:     numCorners = 3;                                 // Split Faces into triangles
    1519:     numPoints  = numVertices + numEdges + numFaces; // total number of coordinate points

    1521:     PetscCall(PetscMalloc2(numPoints * cdim, &coords, numCells * numCorners, &cells));

    1523:     // Get Vertex Coordinates and Set up Cells
    1524:     for (b = 0; b < nbodies; ++b) {
    1525:       ego           body = bodies[b];
    1526:       int           Nf, Ne, Nv;
    1527:       PetscInt      bodyVertexIndexStart, bodyEdgeIndexStart, bodyEdgeGlobalIndexStart, bodyFaceIndexStart;
    1528:       PetscHashIter BViter, BEiter, BEGiter, BFiter, EMiter;
    1529:       PetscBool     BVfound, BEfound, BEGfound, BFfound, EMfound;

    1531:       // Vertices on Current Body
    1532:       if (islite) PetscCall(EGlite_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
    1533:       else PetscCall(EG_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));

    1535:       PetscCall(PetscHMapIFind(bodyVertexMap, b, &BViter, &BVfound));
    1536:       PetscCheck(BVfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %" PetscInt_FMT " not found in bodyVertexMap", b);
    1537:       PetscCall(PetscHMapIGet(bodyVertexMap, b, &bodyVertexIndexStart));

    1539:       for (int v = 0; v < Nv; ++v) {
    1540:         ego    vertex = nobjs[v];
    1541:         double limits[4];
    1542:         int    id, unused;

    1544:         if (islite) {
    1545:           PetscCall(EGlite_getTopology(vertex, &geom, &oclass, &mtype, limits, &unused, &mobjs, &senses));
    1546:           id = EGlite_indexBodyTopo(body, vertex);
    1547:         } else {
    1548:           PetscCall(EG_getTopology(vertex, &geom, &oclass, &mtype, limits, &unused, &mobjs, &senses));
    1549:           id = EG_indexBodyTopo(body, vertex);
    1550:         }

    1552:         coords[(bodyVertexIndexStart + id - 1) * cdim + 0] = limits[0];
    1553:         coords[(bodyVertexIndexStart + id - 1) * cdim + 1] = limits[1];
    1554:         coords[(bodyVertexIndexStart + id - 1) * cdim + 2] = limits[2];
    1555:       }
    1556:       if (islite) {
    1557:         EGlite_free(nobjs);
    1558:       } else {
    1559:         EG_free(nobjs);
    1560:       }

    1562:       // Edge Midpoint Vertices on Current Body
    1563:       if (islite) PetscCall(EGlite_getBodyTopos(body, NULL, EDGE, &Ne, &eobjs));
    1564:       else PetscCall(EG_getBodyTopos(body, NULL, EDGE, &Ne, &eobjs));

    1566:       PetscCall(PetscHMapIFind(bodyEdgeMap, b, &BEiter, &BEfound));
    1567:       PetscCheck(BEfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %" PetscInt_FMT " not found in bodyEdgeMap", b);
    1568:       PetscCall(PetscHMapIGet(bodyEdgeMap, b, &bodyEdgeIndexStart));

    1570:       PetscCall(PetscHMapIFind(bodyEdgeGlobalMap, b, &BEGiter, &BEGfound));
    1571:       PetscCheck(BEGfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %" PetscInt_FMT " not found in bodyEdgeGlobalMap", b);
    1572:       PetscCall(PetscHMapIGet(bodyEdgeGlobalMap, b, &bodyEdgeGlobalIndexStart));

    1574:       for (int e = 0; e < Ne; ++e) {
    1575:         ego    edge = eobjs[e];
    1576:         double range[2], avgt[1], cntrPnt[9];
    1577:         int    eid, eOffset;
    1578:         int    periodic;

    1580:         if (islite) {
    1581:           PetscCall(EGlite_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
    1582:         } else {
    1583:           PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
    1584:         }
    1585:         if (mtype == DEGENERATE) continue;

    1587:         if (islite) {
    1588:           eid = EGlite_indexBodyTopo(body, edge);
    1589:         } else {
    1590:           eid = EG_indexBodyTopo(body, edge);
    1591:         }
    1592:         // get relative offset from globalEdgeID Vector
    1593:         PetscCall(PetscHMapIFind(edgeMap, bodyEdgeGlobalIndexStart + eid - 1, &EMiter, &EMfound));
    1594:         PetscCheck(EMfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Edge %" PetscInt_FMT " not found in edgeMap", bodyEdgeGlobalIndexStart + eid - 1);
    1595:         PetscCall(PetscHMapIGet(edgeMap, bodyEdgeGlobalIndexStart + eid - 1, &eOffset));

    1597:         if (islite) {
    1598:           PetscCall(EGlite_getRange(edge, range, &periodic));
    1599:         } else {
    1600:           PetscCall(EG_getRange(edge, range, &periodic));
    1601:         }
    1602:         avgt[0] = (range[0] + range[1]) / 2.;

    1604:         if (islite) {
    1605:           PetscCall(EGlite_evaluate(edge, avgt, cntrPnt));
    1606:         } else {
    1607:           PetscCall(EG_evaluate(edge, avgt, cntrPnt));
    1608:         }
    1609:         coords[(numVertices + bodyEdgeIndexStart + eOffset - 1) * cdim + 0] = cntrPnt[0];
    1610:         coords[(numVertices + bodyEdgeIndexStart + eOffset - 1) * cdim + 1] = cntrPnt[1];
    1611:         coords[(numVertices + bodyEdgeIndexStart + eOffset - 1) * cdim + 2] = cntrPnt[2];
    1612:       }
    1613:       if (islite) {
    1614:         EGlite_free(eobjs);
    1615:       } else {
    1616:         EG_free(eobjs);
    1617:       }
    1618:       // Face Midpoint Vertices on Current Body
    1619:       if (islite) PetscCall(EGlite_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
    1620:       else PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
    1621:       PetscCall(PetscHMapIFind(bodyFaceMap, b, &BFiter, &BFfound));
    1622:       PetscCheck(BFfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in bodyFaceMap", b);
    1623:       PetscCall(PetscHMapIGet(bodyFaceMap, b, &bodyFaceIndexStart));

    1625:       for (int f = 0; f < Nf; ++f) {
    1626:         ego    face = fobjs[f];
    1627:         double range[4], avgUV[2], cntrPnt[18];
    1628:         int    peri, id;

    1630:         if (islite) {
    1631:           id = EGlite_indexBodyTopo(body, face);
    1632:           PetscCall(EGlite_getRange(face, range, &peri));
    1633:         } else {
    1634:           id = EG_indexBodyTopo(body, face);
    1635:           PetscCall(EG_getRange(face, range, &peri));
    1636:         }

    1638:         avgUV[0] = (range[0] + range[1]) / 2.;
    1639:         avgUV[1] = (range[2] + range[3]) / 2.;

    1641:         if (islite) {
    1642:           PetscCall(EGlite_evaluate(face, avgUV, cntrPnt));
    1643:         } else {
    1644:           PetscCall(EG_evaluate(face, avgUV, cntrPnt));
    1645:         }

    1647:         coords[(numVertices + numEdges + bodyFaceIndexStart + id - 1) * cdim + 0] = cntrPnt[0];
    1648:         coords[(numVertices + numEdges + bodyFaceIndexStart + id - 1) * cdim + 1] = cntrPnt[1];
    1649:         coords[(numVertices + numEdges + bodyFaceIndexStart + id - 1) * cdim + 2] = cntrPnt[2];
    1650:       }
    1651:       if (islite) {
    1652:         EGlite_free(fobjs);
    1653:       } else {
    1654:         EG_free(fobjs);
    1655:       }

    1657:       // Define Cells :: Note - This could be incorporated in the Face Midpoint Vertices Loop but was kept separate for clarity
    1658:       if (islite) PetscCall(EGlite_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
    1659:       else PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
    1660:       for (int f = 0; f < Nf; ++f) {
    1661:         ego face = fobjs[f];
    1662:         int fID, midFaceID, midPntID, startID, endID, Nl;

    1664:         if (islite) {
    1665:           fID = EGlite_indexBodyTopo(body, face);
    1666:         } else {
    1667:           fID = EG_indexBodyTopo(body, face);
    1668:         }

    1670:         midFaceID = numVertices + numEdges + bodyFaceIndexStart + fID - 1;
    1671:         // Must Traverse Loop to ensure we have all necessary information like the sense (+/- 1) of the edges.
    1672:         // TODO :: Only handles single loop faces (No holes). The choices for handling multiloop faces are:
    1673:         //            1) Use the DMPlexCreateGeomFromFile() with the -dm_plex_geom_with_tess = 1 option.
    1674:         //               This will use a default EGADS tessellation as an initial surface mesh.
    1675:         //            2) Create the initial surface mesh via a 2D mesher :: Currently not available (?future?)
    1676:         //               May I suggest the XXXX as a starting point?

    1678:         if (islite) PetscCall(EGlite_getTopology(face, &geom, &oclass, &mtype, NULL, &Nl, &lobjs, &lSenses));
    1679:         else PetscCall(EG_getTopology(face, &geom, &oclass, &mtype, NULL, &Nl, &lobjs, &lSenses));

    1681:         PetscCheck(Nl == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Face has %" PetscInt_FMT " Loops. Can only handle Faces with 1 Loop. Please use --dm_plex_geom_with_tess = 1 Option", Nl);
    1682:         for (int l = 0; l < Nl; ++l) {
    1683:           ego loop = lobjs[l];

    1685:           if (islite) PetscCall(EGlite_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &eobjs, &eSenses));
    1686:           else PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &eobjs, &eSenses));

    1688:           for (int e = 0; e < Ne; ++e) {
    1689:             ego edge = eobjs[e];
    1690:             int eid, eOffset;

    1692:             if (islite) {
    1693:               PetscCall(EGlite_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
    1694:               eid = EGlite_indexBodyTopo(body, edge);
    1695:             } else {
    1696:               PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
    1697:               eid = EG_indexBodyTopo(body, edge);
    1698:             }
    1699:             if (mtype == DEGENERATE) continue;

    1701:             // get relative offset from globalEdgeID Vector
    1702:             PetscCall(PetscHMapIFind(edgeMap, bodyEdgeGlobalIndexStart + eid - 1, &EMiter, &EMfound));
    1703:             PetscCheck(EMfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Edge %" PetscInt_FMT " of Body %" PetscInt_FMT " not found in edgeMap. Global Edge ID :: %" PetscInt_FMT, eid, b, bodyEdgeGlobalIndexStart + eid - 1);
    1704:             PetscCall(PetscHMapIGet(edgeMap, bodyEdgeGlobalIndexStart + eid - 1, &eOffset));

    1706:             midPntID = numVertices + bodyEdgeIndexStart + eOffset - 1;

    1708:             if (islite) PetscCall(EGlite_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
    1709:             else PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));

    1711:             if (eSenses[e] > 0) {
    1712:               if (islite) {
    1713:                 startID = EGlite_indexBodyTopo(body, nobjs[0]);
    1714:                 endID   = EGlite_indexBodyTopo(body, nobjs[1]);
    1715:               } else {
    1716:                 startID = EG_indexBodyTopo(body, nobjs[0]);
    1717:                 endID   = EG_indexBodyTopo(body, nobjs[1]);
    1718:               }
    1719:             } else {
    1720:               if (islite) {
    1721:                 startID = EGlite_indexBodyTopo(body, nobjs[1]);
    1722:                 endID   = EGlite_indexBodyTopo(body, nobjs[0]);
    1723:               } else {
    1724:                 startID = EG_indexBodyTopo(body, nobjs[1]);
    1725:                 endID   = EG_indexBodyTopo(body, nobjs[0]);
    1726:               }
    1727:             }

    1729:             // Define 2 Cells per Edge with correct orientation
    1730:             cells[cellCntr * numCorners + 0] = midFaceID;
    1731:             cells[cellCntr * numCorners + 1] = bodyVertexIndexStart + startID - 1;
    1732:             cells[cellCntr * numCorners + 2] = midPntID;

    1734:             cells[cellCntr * numCorners + 3] = midFaceID;
    1735:             cells[cellCntr * numCorners + 4] = midPntID;
    1736:             cells[cellCntr * numCorners + 5] = bodyVertexIndexStart + endID - 1;

    1738:             cellCntr = cellCntr + 2;
    1739:           }
    1740:         }
    1741:       }
    1742:       if (islite) {
    1743:         EGlite_free(fobjs);
    1744:       } else {
    1745:         EG_free(fobjs);
    1746:       }
    1747:     }
    1748:   }

    1750:   // Generate DMPlex
    1751:   PetscCall(DMPlexCreateFromCellListPetsc(PETSC_COMM_WORLD, dim, numCells, numPoints, numCorners, PETSC_TRUE, cells, cdim, coords, &dm));
    1752:   PetscCall(PetscFree2(coords, cells));
    1753:   PetscCall(PetscInfo(dm, " Total Number of Unique Cells    = %" PetscInt_FMT " \n", numCells));
    1754:   PetscCall(PetscInfo(dm, " Total Number of Unique Vertices = %" PetscInt_FMT " \n", numVertices));

    1756:   // Embed EGADS model in DM
    1757:   {
    1758:     PetscContainer modelObj, contextObj;

    1760:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &modelObj));
    1761:     PetscCall(PetscContainerSetPointer(modelObj, model));
    1762:     if (islite) {
    1763:       PetscCall(PetscContainerSetCtxDestroy(modelObj, DMPlexEGADSliteDestroy_Private));
    1764:       PetscCall(PetscObjectCompose((PetscObject)dm, "EGADSlite Model", (PetscObject)modelObj));
    1765:     } else {
    1766:       PetscCall(PetscContainerSetCtxDestroy(modelObj, DMPlexEGADSDestroy_Private));
    1767:       PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Model", (PetscObject)modelObj));
    1768:     }
    1769:     PetscCall(PetscContainerDestroy(&modelObj));

    1771:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &contextObj));
    1772:     PetscCall(PetscContainerSetPointer(contextObj, context));

    1774:     if (islite) {
    1775:       PetscCall(PetscContainerSetCtxDestroy(contextObj, DMPlexEGADSliteClose_Private));
    1776:       PetscCall(PetscObjectCompose((PetscObject)dm, "EGADSlite Context", (PetscObject)contextObj));
    1777:     } else {
    1778:       PetscCall(PetscContainerSetCtxDestroy(contextObj, DMPlexEGADSClose_Private));
    1779:       PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Context", (PetscObject)contextObj));
    1780:     }
    1781:     PetscCall(PetscContainerDestroy(&contextObj));
    1782:   }
    1783:   // Label points
    1784:   PetscInt nStart, nEnd;

    1786:   PetscCall(DMCreateLabel(dm, "EGADS Body ID"));
    1787:   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
    1788:   PetscCall(DMCreateLabel(dm, "EGADS Face ID"));
    1789:   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
    1790:   PetscCall(DMCreateLabel(dm, "EGADS Edge ID"));
    1791:   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
    1792:   PetscCall(DMCreateLabel(dm, "EGADS Vertex ID"));
    1793:   PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));

    1795:   PetscCall(DMPlexGetHeightStratum(dm, 2, &nStart, &nEnd));

    1797:   cellCntr = 0;
    1798:   for (b = 0; b < nbodies; ++b) {
    1799:     ego           body = bodies[b];
    1800:     int           Nv, Ne, Nf;
    1801:     PetscInt      bodyVertexIndexStart, bodyEdgeIndexStart, bodyEdgeGlobalIndexStart, bodyFaceIndexStart;
    1802:     PetscHashIter BViter, BEiter, BEGiter, BFiter, EMiter;
    1803:     PetscBool     BVfound, BEfound, BEGfound, BFfound, EMfound;

    1805:     PetscCall(PetscHMapIFind(bodyVertexMap, b, &BViter, &BVfound));
    1806:     PetscCheck(BVfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in bodyVertexMap", b);
    1807:     PetscCall(PetscHMapIGet(bodyVertexMap, b, &bodyVertexIndexStart));

    1809:     PetscCall(PetscHMapIFind(bodyEdgeMap, b, &BEiter, &BEfound));
    1810:     PetscCheck(BEfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in bodyEdgeMap", b);
    1811:     PetscCall(PetscHMapIGet(bodyEdgeMap, b, &bodyEdgeIndexStart));

    1813:     PetscCall(PetscHMapIFind(bodyFaceMap, b, &BFiter, &BFfound));
    1814:     PetscCheck(BFfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in bodyFaceMap", b);
    1815:     PetscCall(PetscHMapIGet(bodyFaceMap, b, &bodyFaceIndexStart));

    1817:     PetscCall(PetscHMapIFind(bodyEdgeGlobalMap, b, &BEGiter, &BEGfound));
    1818:     PetscCheck(BEGfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in bodyEdgeGlobalMap", b);
    1819:     PetscCall(PetscHMapIGet(bodyEdgeGlobalMap, b, &bodyEdgeGlobalIndexStart));

    1821:     if (islite) PetscCall(EGlite_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
    1822:     else PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));

    1824:     for (int f = 0; f < Nf; ++f) {
    1825:       ego face = fobjs[f];
    1826:       int fID, Nl;

    1828:       if (islite) {
    1829:         fID = EGlite_indexBodyTopo(body, face);
    1830:         PetscCall(EGlite_getBodyTopos(body, face, LOOP, &Nl, &lobjs));
    1831:       } else {
    1832:         fID = EG_indexBodyTopo(body, face);
    1833:         PetscCall(EG_getBodyTopos(body, face, LOOP, &Nl, &lobjs));
    1834:       }

    1836:       for (int l = 0; l < Nl; ++l) {
    1837:         ego loop = lobjs[l];
    1838:         int lid;

    1840:         if (islite) {
    1841:           lid = EGlite_indexBodyTopo(body, loop);
    1842:         } else {
    1843:           lid = EG_indexBodyTopo(body, loop);
    1844:         }

    1846:         PetscCheck(Nl == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Loop %" PetscInt_FMT " has %" PetscInt_FMT " > 1 faces, which is not supported", lid, Nf);

    1848:         if (islite) PetscCall(EGlite_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &eobjs, &eSenses));
    1849:         else PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &eobjs, &eSenses));

    1851:         for (int e = 0; e < Ne; ++e) {
    1852:           ego edge = eobjs[e];
    1853:           int eid, eOffset;

    1855:           // Skip DEGENERATE Edges
    1856:           if (islite) {
    1857:             PetscCall(EGlite_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
    1858:           } else {
    1859:             PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
    1860:           }

    1862:           if (mtype == DEGENERATE) continue;

    1864:           if (islite) {
    1865:             eid = EGlite_indexBodyTopo(body, edge);
    1866:           } else {
    1867:             eid = EG_indexBodyTopo(body, edge);
    1868:           }

    1870:           // get relative offset from globalEdgeID Vector
    1871:           PetscCall(PetscHMapIFind(edgeMap, bodyEdgeGlobalIndexStart + eid - 1, &EMiter, &EMfound));
    1872:           PetscCheck(EMfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Edge %" PetscInt_FMT " of Body %" PetscInt_FMT " not found in edgeMap. Global Edge ID :: %" PetscInt_FMT, eid, b, bodyEdgeGlobalIndexStart + eid - 1);
    1873:           PetscCall(PetscHMapIGet(edgeMap, bodyEdgeGlobalIndexStart + eid - 1, &eOffset));

    1875:           if (islite) PetscCall(EGlite_getBodyTopos(body, edge, NODE, &Nv, &nobjs));
    1876:           else PetscCall(EG_getBodyTopos(body, edge, NODE, &Nv, &nobjs));

    1878:           for (int v = 0; v < Nv; ++v) {
    1879:             ego vertex = nobjs[v];
    1880:             int vID;

    1882:             if (islite) {
    1883:               vID = EGlite_indexBodyTopo(body, vertex);
    1884:             } else {
    1885:               vID = EG_indexBodyTopo(body, vertex);
    1886:             }

    1888:             PetscCall(DMLabelSetValue(bodyLabel, nStart + bodyVertexIndexStart + vID - 1, b));
    1889:             PetscCall(DMLabelSetValue(vertexLabel, nStart + bodyVertexIndexStart + vID - 1, vID));
    1890:           }
    1891:           if (islite) {
    1892:             EGlite_free(nobjs);
    1893:           } else {
    1894:             EG_free(nobjs);
    1895:           }

    1897:           PetscCall(DMLabelSetValue(bodyLabel, nStart + numVertices + bodyEdgeIndexStart + eOffset - 1, b));
    1898:           PetscCall(DMLabelSetValue(edgeLabel, nStart + numVertices + bodyEdgeIndexStart + eOffset - 1, eid));

    1900:           // Define Cell faces
    1901:           for (int jj = 0; jj < 2; ++jj) {
    1902:             PetscCall(DMLabelSetValue(bodyLabel, cellCntr, b));
    1903:             PetscCall(DMLabelSetValue(faceLabel, cellCntr, fID));
    1904:             PetscCall(DMPlexGetCone(dm, cellCntr, &cone));

    1906:             PetscCall(DMLabelSetValue(bodyLabel, cone[0], b));
    1907:             PetscCall(DMLabelSetValue(faceLabel, cone[0], fID));

    1909:             PetscCall(DMLabelSetValue(bodyLabel, cone[1], b));
    1910:             PetscCall(DMLabelSetValue(edgeLabel, cone[1], eid));

    1912:             PetscCall(DMLabelSetValue(bodyLabel, cone[2], b));
    1913:             PetscCall(DMLabelSetValue(faceLabel, cone[2], fID));

    1915:             cellCntr = cellCntr + 1;
    1916:           }
    1917:         }
    1918:       }
    1919:       if (islite) {
    1920:         EGlite_free(lobjs);
    1921:       } else {
    1922:         EG_free(lobjs);
    1923:       }

    1925:       PetscCall(DMLabelSetValue(bodyLabel, nStart + numVertices + numEdges + bodyFaceIndexStart + fID - 1, b));
    1926:       PetscCall(DMLabelSetValue(faceLabel, nStart + numVertices + numEdges + bodyFaceIndexStart + fID - 1, fID));
    1927:     }
    1928:     if (islite) {
    1929:       EGlite_free(fobjs);
    1930:     } else {
    1931:       EG_free(fobjs);
    1932:     }
    1933:   }

    1935:   PetscCall(PetscHMapIDestroy(&edgeMap));
    1936:   PetscCall(PetscHMapIDestroy(&bodyIndexMap));
    1937:   PetscCall(PetscHMapIDestroy(&bodyVertexMap));
    1938:   PetscCall(PetscHMapIDestroy(&bodyEdgeMap));
    1939:   PetscCall(PetscHMapIDestroy(&bodyEdgeGlobalMap));
    1940:   PetscCall(PetscHMapIDestroy(&bodyFaceMap));

    1942:   *newdm = dm;
    1943:   PetscFunctionReturn(PETSC_SUCCESS);
    1944: }

    1946: PetscErrorCode DMPlexCreateGeom_Tess_Internal(MPI_Comm comm, ego context, ego model, DM *newdm, PetscBool islite)
    1947: {
    1948:   /* EGADSlite variables */
    1949:   ego    geom, *bodies, *fobjs;
    1950:   int    b, oclass, mtype, nbodies, *senses;
    1951:   int    totalNumTris = 0, totalNumPoints = 0;
    1952:   double boundBox[6] = {0., 0., 0., 0., 0., 0.}, tessSize;
    1953:   /* PETSc variables */
    1954:   DM              dm;
    1955:   DMLabel         bodyLabel, faceLabel, edgeLabel, vertexLabel;
    1956:   PetscHMapI      pointIndexStartMap = NULL, triIndexStartMap = NULL, pTypeLabelMap = NULL, pIndexLabelMap = NULL;
    1957:   PetscHMapI      pBodyIndexLabelMap = NULL, triFaceIDLabelMap = NULL, triBodyIDLabelMap = NULL;
    1958:   PetscInt        dim = -1, cdim = -1, numCorners = 0, counter = 0;
    1959:   PetscInt       *cells  = NULL;
    1960:   const PetscInt *cone   = NULL;
    1961:   PetscReal      *coords = NULL;
    1962:   PetscMPIInt     rank;

    1964:   PetscFunctionBeginUser;
    1965:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
    1966:   if (rank == 0) {
    1967:     // Generate PETSc DMPlex from EGADSlite created Tessellation of geometry

    1969:     // Calculate cell and vertex sizes
    1970:     if (islite) PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, &nbodies, &bodies, &senses));
    1971:     else PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &nbodies, &bodies, &senses));

    1973:     PetscCall(PetscHMapICreate(&pointIndexStartMap));
    1974:     PetscCall(PetscHMapICreate(&triIndexStartMap));
    1975:     PetscCall(PetscHMapICreate(&pTypeLabelMap));
    1976:     PetscCall(PetscHMapICreate(&pIndexLabelMap));
    1977:     PetscCall(PetscHMapICreate(&pBodyIndexLabelMap));
    1978:     PetscCall(PetscHMapICreate(&triFaceIDLabelMap));
    1979:     PetscCall(PetscHMapICreate(&triBodyIDLabelMap));

    1981:     /* Create Tessellation of Bodies */
    1982:     ego *tessArray;

    1984:     PetscCall(PetscMalloc1(nbodies, &tessArray));
    1985:     for (b = 0; b < nbodies; ++b) {
    1986:       ego           body      = bodies[b];
    1987:       double        params[3] = {0.0, 0.0, 0.0}; // Parameters for Tessellation
    1988:       int           Nf, bodyNumPoints = 0, bodyNumTris = 0;
    1989:       PetscHashIter PISiter, TISiter;
    1990:       PetscBool     PISfound, TISfound;

    1992:       /* Store Start Index for each Body's Point and Tris */
    1993:       PetscCall(PetscHMapIFind(pointIndexStartMap, b, &PISiter, &PISfound));
    1994:       PetscCall(PetscHMapIFind(triIndexStartMap, b, &TISiter, &TISfound));

    1996:       if (!PISfound) PetscCall(PetscHMapISet(pointIndexStartMap, b, totalNumPoints));
    1997:       if (!TISfound) PetscCall(PetscHMapISet(triIndexStartMap, b, totalNumTris));

    1999:       /* Calculate Tessellation parameters based on Bounding Box */
    2000:       /* Get Bounding Box Dimensions of the BODY */
    2001:       if (islite) PetscCall(EGlite_getBoundingBox(body, boundBox));
    2002:       else PetscCall(EG_getBoundingBox(body, boundBox));

    2004:       tessSize = boundBox[3] - boundBox[0];
    2005:       if (tessSize < boundBox[4] - boundBox[1]) tessSize = boundBox[4] - boundBox[1];
    2006:       if (tessSize < boundBox[5] - boundBox[2]) tessSize = boundBox[5] - boundBox[2];

    2008:       // TODO :: May want to give users tessellation parameter options //
    2009:       params[0] = 0.0250 * tessSize;
    2010:       params[1] = 0.0075 * tessSize;
    2011:       params[2] = 15.0;

    2013:       if (islite) {
    2014:         PetscCall(EGlite_makeTessBody(body, params, &tessArray[b]));
    2015:         PetscCall(EGlite_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
    2016:       } else {
    2017:         PetscCall(EG_makeTessBody(body, params, &tessArray[b]));
    2018:         PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
    2019:       }

    2021:       for (int f = 0; f < Nf; ++f) {
    2022:         ego           face = fobjs[f];
    2023:         int           len, fID, ntris;
    2024:         const int    *ptype, *pindex, *ptris, *ptric;
    2025:         const double *pxyz, *puv;

    2027:         // Get Face ID //
    2028:         if (islite) {
    2029:           fID = EGlite_indexBodyTopo(body, face);
    2030:         } else {
    2031:           fID = EG_indexBodyTopo(body, face);
    2032:         }

    2034:         // Checkout the Surface Tessellation //
    2035:         if (islite) {
    2036:           PetscCall(EGlite_getTessFace(tessArray[b], fID, &len, &pxyz, &puv, &ptype, &pindex, &ntris, &ptris, &ptric));
    2037:         } else {
    2038:           PetscCall(EG_getTessFace(tessArray[b], fID, &len, &pxyz, &puv, &ptype, &pindex, &ntris, &ptris, &ptric));
    2039:         }

    2041:         // Determine total number of triangle cells in the tessellation //
    2042:         bodyNumTris += (int)ntris;

    2044:         // Check out the point index and coordinate //
    2045:         for (int p = 0; p < len; ++p) {
    2046:           int global;

    2048:           if (islite) {
    2049:             PetscCall(EGlite_localToGlobal(tessArray[b], fID, p + 1, &global));
    2050:           } else {
    2051:             PetscCall(EG_localToGlobal(tessArray[b], fID, p + 1, &global));
    2052:           }

    2054:           // Determine the total number of points in the tessellation //
    2055:           bodyNumPoints = PetscMax(bodyNumPoints, global);
    2056:         }
    2057:       }
    2058:       if (islite) {
    2059:         EGlite_free(fobjs);
    2060:       } else {
    2061:         EG_free(fobjs);
    2062:       }

    2064:       totalNumPoints += bodyNumPoints;
    2065:       totalNumTris += bodyNumTris;
    2066:     }

    2068:     dim        = 2;
    2069:     cdim       = 3;
    2070:     numCorners = 3;

    2072:     /* NEED TO DEFINE MATRICES/VECTORS TO STORE GEOM REFERENCE DATA   */
    2073:     /* Fill in below and use to define DMLabels after DMPlex creation */
    2074:     PetscCall(PetscMalloc2(totalNumPoints * cdim, &coords, totalNumTris * numCorners, &cells));

    2076:     for (b = 0; b < nbodies; ++b) {
    2077:       ego           body = bodies[b];
    2078:       int           Nf;
    2079:       PetscInt      pointIndexStart;
    2080:       PetscHashIter PISiter;
    2081:       PetscBool     PISfound;

    2083:       PetscCall(PetscHMapIFind(pointIndexStartMap, b, &PISiter, &PISfound));
    2084:       PetscCheck(PISfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %" PetscInt_FMT " not found in pointIndexStartMap", b);
    2085:       PetscCall(PetscHMapIGet(pointIndexStartMap, b, &pointIndexStart));

    2087:       if (islite) PetscCall(EGlite_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
    2088:       else PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));

    2090:       for (int f = 0; f < Nf; ++f) {
    2091:         /* Get Face Object */
    2092:         ego           face = fobjs[f];
    2093:         int           len, fID, ntris;
    2094:         const int    *ptype, *pindex, *ptris, *ptric;
    2095:         const double *pxyz, *puv;

    2097:         /* Get Face ID */
    2098:         if (islite) {
    2099:           fID = EGlite_indexBodyTopo(body, face);
    2100:         } else {
    2101:           fID = EG_indexBodyTopo(body, face);
    2102:         }

    2104:         /* Checkout the Surface Tessellation */
    2105:         if (islite) {
    2106:           PetscCall(EGlite_getTessFace(tessArray[b], fID, &len, &pxyz, &puv, &ptype, &pindex, &ntris, &ptris, &ptric));
    2107:         } else {
    2108:           PetscCall(EG_getTessFace(tessArray[b], fID, &len, &pxyz, &puv, &ptype, &pindex, &ntris, &ptris, &ptric));
    2109:         }

    2111:         /* Check out the point index and coordinate */
    2112:         for (int p = 0; p < len; ++p) {
    2113:           int           global;
    2114:           PetscHashIter PTLiter, PILiter, PBLiter;
    2115:           PetscBool     PTLfound, PILfound, PBLfound;

    2117:           if (islite) {
    2118:             PetscCall(EGlite_localToGlobal(tessArray[b], fID, p + 1, &global));
    2119:           } else {
    2120:             PetscCall(EG_localToGlobal(tessArray[b], fID, p + 1, &global));
    2121:           }

    2123:           /* Set the coordinates array for DAG */
    2124:           coords[((global - 1 + pointIndexStart) * 3) + 0] = pxyz[(p * 3) + 0];
    2125:           coords[((global - 1 + pointIndexStart) * 3) + 1] = pxyz[(p * 3) + 1];
    2126:           coords[((global - 1 + pointIndexStart) * 3) + 2] = pxyz[(p * 3) + 2];

    2128:           /* Store Geometry Label Information for DMLabel assignment later */
    2129:           PetscCall(PetscHMapIFind(pTypeLabelMap, global - 1 + pointIndexStart, &PTLiter, &PTLfound));
    2130:           PetscCall(PetscHMapIFind(pIndexLabelMap, global - 1 + pointIndexStart, &PILiter, &PILfound));
    2131:           PetscCall(PetscHMapIFind(pBodyIndexLabelMap, global - 1 + pointIndexStart, &PBLiter, &PBLfound));

    2133:           if (!PTLfound) PetscCall(PetscHMapISet(pTypeLabelMap, global - 1 + pointIndexStart, ptype[p]));
    2134:           if (!PILfound) PetscCall(PetscHMapISet(pIndexLabelMap, global - 1 + pointIndexStart, pindex[p]));
    2135:           if (!PBLfound) PetscCall(PetscHMapISet(pBodyIndexLabelMap, global - 1 + pointIndexStart, b));

    2137:           if (ptype[p] < 0) PetscCall(PetscHMapISet(pIndexLabelMap, global - 1 + pointIndexStart, fID));
    2138:         }

    2140:         for (int t = 0; t < (int)ntris; ++t) {
    2141:           int           global, globalA, globalB;
    2142:           PetscHashIter TFLiter, TBLiter;
    2143:           PetscBool     TFLfound, TBLfound;

    2145:           if (islite) {
    2146:             PetscCall(EGlite_localToGlobal(tessArray[b], fID, ptris[(t * 3) + 0], &global));
    2147:           } else {
    2148:             PetscCall(EG_localToGlobal(tessArray[b], fID, ptris[(t * 3) + 0], &global));
    2149:           }
    2150:           cells[(counter * 3) + 0] = global - 1 + pointIndexStart;

    2152:           if (islite) {
    2153:             PetscCall(EGlite_localToGlobal(tessArray[b], fID, ptris[(t * 3) + 1], &globalA));
    2154:           } else {
    2155:             PetscCall(EG_localToGlobal(tessArray[b], fID, ptris[(t * 3) + 1], &globalA));
    2156:           }
    2157:           cells[(counter * 3) + 1] = globalA - 1 + pointIndexStart;

    2159:           if (islite) {
    2160:             PetscCall(EGlite_localToGlobal(tessArray[b], fID, ptris[(t * 3) + 2], &globalB));
    2161:           } else {
    2162:             PetscCall(EG_localToGlobal(tessArray[b], fID, ptris[(t * 3) + 2], &globalB));
    2163:           }
    2164:           cells[(counter * 3) + 2] = globalB - 1 + pointIndexStart;

    2166:           PetscCall(PetscHMapIFind(triFaceIDLabelMap, counter, &TFLiter, &TFLfound));
    2167:           PetscCall(PetscHMapIFind(triBodyIDLabelMap, counter, &TBLiter, &TBLfound));

    2169:           if (!TFLfound) PetscCall(PetscHMapISet(triFaceIDLabelMap, counter, fID));
    2170:           if (!TBLfound) PetscCall(PetscHMapISet(triBodyIDLabelMap, counter, b));

    2172:           counter += 1;
    2173:         }
    2174:       }
    2175:       if (islite) {
    2176:         EGlite_free(fobjs);
    2177:       } else {
    2178:         EG_free(fobjs);
    2179:       }
    2180:     }
    2181:     PetscCall(PetscFree(tessArray));
    2182:   }

    2184:   //Build DMPlex
    2185:   PetscCall(DMPlexCreateFromCellListPetsc(PETSC_COMM_WORLD, dim, totalNumTris, totalNumPoints, numCorners, PETSC_TRUE, cells, cdim, coords, &dm));
    2186:   PetscCall(PetscFree2(coords, cells));

    2188:   // Embed EGADS model in DM
    2189:   {
    2190:     PetscContainer modelObj, contextObj;

    2192:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &modelObj));
    2193:     PetscCall(PetscContainerSetPointer(modelObj, model));
    2194:     if (islite) {
    2195:       PetscCall(PetscContainerSetCtxDestroy(modelObj, DMPlexEGADSliteDestroy_Private));
    2196:       PetscCall(PetscObjectCompose((PetscObject)dm, "EGADSlite Model", (PetscObject)modelObj));
    2197:     } else {
    2198:       PetscCall(PetscContainerSetCtxDestroy(modelObj, DMPlexEGADSDestroy_Private));
    2199:       PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Model", (PetscObject)modelObj));
    2200:     }
    2201:     PetscCall(PetscContainerDestroy(&modelObj));

    2203:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &contextObj));
    2204:     PetscCall(PetscContainerSetPointer(contextObj, context));

    2206:     if (islite) {
    2207:       PetscCall(PetscContainerSetCtxDestroy(contextObj, DMPlexEGADSliteClose_Private));
    2208:       PetscCall(PetscObjectCompose((PetscObject)dm, "EGADSlite Context", (PetscObject)contextObj));
    2209:     } else {
    2210:       PetscCall(PetscContainerSetCtxDestroy(contextObj, DMPlexEGADSClose_Private));
    2211:       PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Context", (PetscObject)contextObj));
    2212:     }
    2213:     PetscCall(PetscContainerDestroy(&contextObj));
    2214:   }

    2216:   // Label Points
    2217:   PetscCall(DMCreateLabel(dm, "EGADS Body ID"));
    2218:   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
    2219:   PetscCall(DMCreateLabel(dm, "EGADS Face ID"));
    2220:   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
    2221:   PetscCall(DMCreateLabel(dm, "EGADS Edge ID"));
    2222:   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
    2223:   PetscCall(DMCreateLabel(dm, "EGADS Vertex ID"));
    2224:   PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));

    2226:   /* Get Number of DAG Nodes at each level */
    2227:   int fStart, fEnd, eStart, eEnd, nStart, nEnd;

    2229:   PetscCall(DMPlexGetHeightStratum(dm, 0, &fStart, &fEnd));
    2230:   PetscCall(DMPlexGetHeightStratum(dm, 1, &eStart, &eEnd));
    2231:   PetscCall(DMPlexGetHeightStratum(dm, 2, &nStart, &nEnd));

    2233:   /* Set DMLabels for NODES */
    2234:   for (int n = nStart; n < nEnd; ++n) {
    2235:     int           pTypeVal, pIndexVal, pBodyVal;
    2236:     PetscHashIter PTLiter, PILiter, PBLiter;
    2237:     PetscBool     PTLfound, PILfound, PBLfound;

    2239:     //Converted to Hash Tables
    2240:     PetscCall(PetscHMapIFind(pTypeLabelMap, n - nStart, &PTLiter, &PTLfound));
    2241:     PetscCheck(PTLfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "DAG Point %" PetscInt_FMT " not found in pTypeLabelMap", n);
    2242:     PetscCall(PetscHMapIGet(pTypeLabelMap, n - nStart, &pTypeVal));

    2244:     PetscCall(PetscHMapIFind(pIndexLabelMap, n - nStart, &PILiter, &PILfound));
    2245:     PetscCheck(PILfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "DAG Point %" PetscInt_FMT " not found in pIndexLabelMap", n);
    2246:     PetscCall(PetscHMapIGet(pIndexLabelMap, n - nStart, &pIndexVal));

    2248:     PetscCall(PetscHMapIFind(pBodyIndexLabelMap, n - nStart, &PBLiter, &PBLfound));
    2249:     PetscCheck(PBLfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "DAG Point %" PetscInt_FMT " not found in pBodyLabelMap", n);
    2250:     PetscCall(PetscHMapIGet(pBodyIndexLabelMap, n - nStart, &pBodyVal));

    2252:     PetscCall(DMLabelSetValue(bodyLabel, n, pBodyVal));
    2253:     if (pTypeVal == 0) PetscCall(DMLabelSetValue(vertexLabel, n, pIndexVal));
    2254:     if (pTypeVal > 0) PetscCall(DMLabelSetValue(edgeLabel, n, pIndexVal));
    2255:     if (pTypeVal < 0) PetscCall(DMLabelSetValue(faceLabel, n, pIndexVal));
    2256:   }

    2258:   /* Set DMLabels for Edges - Based on the DMLabels of the EDGE's NODES */
    2259:   for (int e = eStart; e < eEnd; ++e) {
    2260:     int bodyID_0, vertexID_0, vertexID_1, edgeID_0, edgeID_1, faceID_0, faceID_1;

    2262:     PetscCall(DMPlexGetCone(dm, e, &cone));
    2263:     PetscCall(DMLabelGetValue(bodyLabel, cone[0], &bodyID_0)); // Do I need to check the other end?
    2264:     PetscCall(DMLabelGetValue(vertexLabel, cone[0], &vertexID_0));
    2265:     PetscCall(DMLabelGetValue(vertexLabel, cone[1], &vertexID_1));
    2266:     PetscCall(DMLabelGetValue(edgeLabel, cone[0], &edgeID_0));
    2267:     PetscCall(DMLabelGetValue(edgeLabel, cone[1], &edgeID_1));
    2268:     PetscCall(DMLabelGetValue(faceLabel, cone[0], &faceID_0));
    2269:     PetscCall(DMLabelGetValue(faceLabel, cone[1], &faceID_1));

    2271:     PetscCall(DMLabelSetValue(bodyLabel, e, bodyID_0));

    2273:     if (edgeID_0 == edgeID_1) PetscCall(DMLabelSetValue(edgeLabel, e, edgeID_0));
    2274:     else if (vertexID_0 > 0 && edgeID_1 > 0) PetscCall(DMLabelSetValue(edgeLabel, e, edgeID_1));
    2275:     else if (vertexID_1 > 0 && edgeID_0 > 0) PetscCall(DMLabelSetValue(edgeLabel, e, edgeID_0));
    2276:     else { /* Do Nothing */ }
    2277:   }

    2279:   /* Set DMLabels for Cells */
    2280:   for (int f = fStart; f < fEnd; ++f) {
    2281:     int           edgeID_0;
    2282:     PetscInt      triBodyVal, triFaceVal;
    2283:     PetscHashIter TFLiter, TBLiter;
    2284:     PetscBool     TFLfound, TBLfound;

    2286:     // Convert to Hash Table
    2287:     PetscCall(PetscHMapIFind(triFaceIDLabelMap, f - fStart, &TFLiter, &TFLfound));
    2288:     PetscCheck(TFLfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "DAG Point %" PetscInt_FMT " not found in triFaceIDLabelMap", f);
    2289:     PetscCall(PetscHMapIGet(triFaceIDLabelMap, f - fStart, &triFaceVal));

    2291:     PetscCall(PetscHMapIFind(triBodyIDLabelMap, f - fStart, &TBLiter, &TBLfound));
    2292:     PetscCheck(TBLfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "DAG Point %" PetscInt_FMT " not found in triBodyIDLabelMap", f);
    2293:     PetscCall(PetscHMapIGet(triBodyIDLabelMap, f - fStart, &triBodyVal));

    2295:     PetscCall(DMLabelSetValue(bodyLabel, f, triBodyVal));
    2296:     PetscCall(DMLabelSetValue(faceLabel, f, triFaceVal));

    2298:     /* Finish Labeling previously unlabeled DMPlex Edges - Assumes Triangular Cell (3 Edges Max) */
    2299:     PetscCall(DMPlexGetCone(dm, f, &cone));

    2301:     for (int jj = 0; jj < 3; ++jj) {
    2302:       PetscCall(DMLabelGetValue(edgeLabel, cone[jj], &edgeID_0));

    2304:       if (edgeID_0 < 0) {
    2305:         PetscCall(DMLabelSetValue(bodyLabel, cone[jj], triBodyVal));
    2306:         PetscCall(DMLabelSetValue(faceLabel, cone[jj], triFaceVal));
    2307:       }
    2308:     }
    2309:   }

    2311:   *newdm = dm;
    2312:   PetscFunctionReturn(PETSC_SUCCESS);
    2313: }
    2314: #endif

    2316: /*@C
    2317:   DMPlexInflateToGeomModelUseXYZ - Snaps the vertex coordinates of a `DMPLEX` object representing the mesh to its geometry if some vertices depart from the model. This usually happens with non-conforming refinement.

    2319:   Collective

    2321:   Input Parameter:
    2322: . dm - The uninflated `DM` object representing the mesh

    2324:   Level: intermediate

    2326: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexCreateEGADS()`
    2327: @*/
    2328: PetscErrorCode DMPlexInflateToGeomModelUseXYZ(DM dm) PeNS
    2329: {
    2330:   // please don't fucking write code like this with #ifdef all of the place!
    2331: #if defined(PETSC_HAVE_EGADS)
    2332:   /* EGADS Variables */
    2333:   ego    model, geom, body, face, edge, vertex;
    2334:   ego   *bodies;
    2335:   int    Nb, oclass, mtype, *senses;
    2336:   double result[4];
    2337:   /* PETSc Variables */
    2338:   DM             cdm;
    2339:   PetscContainer modelObj;
    2340:   DMLabel        bodyLabel, faceLabel, edgeLabel, vertexLabel;
    2341:   Vec            coordinates;
    2342:   PetscScalar   *coords;
    2343:   PetscInt       bodyID, faceID, edgeID, vertexID;
    2344:   PetscInt       cdim, d, vStart, vEnd, v;
    2345:   PetscBool      islite = PETSC_FALSE;
    2346: #endif

    2348:   PetscFunctionBegin;
    2349: #if defined(PETSC_HAVE_EGADS)
    2350:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
    2351:   if (!modelObj) {
    2352:     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
    2353:     islite = PETSC_TRUE;
    2354:   }
    2355:   if (!modelObj) PetscFunctionReturn(PETSC_SUCCESS);
    2356:   PetscCall(DMGetCoordinateDim(dm, &cdim));
    2357:   PetscCall(DMGetCoordinateDM(dm, &cdm));
    2358:   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
    2359:   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
    2360:   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
    2361:   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
    2362:   PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));

    2364:   PetscCall(PetscContainerGetPointer(modelObj, &model));

    2366:   if (islite) PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
    2367:   else PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));

    2369:   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
    2370:   PetscCall(VecGetArrayWrite(coordinates, &coords));
    2371:   for (v = vStart; v < vEnd; ++v) {
    2372:     PetscScalar *vcoords;

    2374:     PetscCall(DMLabelGetValue(bodyLabel, v, &bodyID));
    2375:     PetscCall(DMLabelGetValue(faceLabel, v, &faceID));
    2376:     PetscCall(DMLabelGetValue(edgeLabel, v, &edgeID));
    2377:     PetscCall(DMLabelGetValue(vertexLabel, v, &vertexID));

    2379:     PetscCheck(bodyID < Nb, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Body %" PetscInt_FMT " is not in [0, %" PetscInt_FMT ")", bodyID, Nb);
    2380:     body = bodies[bodyID];

    2382:     PetscCall(DMPlexPointLocalRef(cdm, v, coords, (void *)&vcoords));
    2383:     if (vertexID > 0) {
    2384:       if (islite) {
    2385:         PetscCall(EGlite_objectBodyTopo(body, NODE, vertexID, &vertex));
    2386:         PetscCall(EGlite_evaluate(vertex, NULL, result));
    2387:       } else {
    2388:         PetscCall(EG_objectBodyTopo(body, NODE, vertexID, &vertex));
    2389:         PetscCall(EG_evaluate(vertex, NULL, result));
    2390:       }
    2391:       for (d = 0; d < cdim; ++d) vcoords[d] = result[d];
    2392:     } else if (edgeID > 0) {
    2393:       /* Snap to EDGE at nearest location */
    2394:       double params[1];
    2395:       if (islite) {
    2396:         PetscCall(EGlite_objectBodyTopo(body, EDGE, edgeID, &edge));
    2397:         PetscCall(EGlite_invEvaluate(edge, vcoords, params, result));
    2398:       } // Get (x,y,z) of nearest point on EDGE
    2399:       else {
    2400:         PetscCall(EG_objectBodyTopo(body, EDGE, edgeID, &edge));
    2401:         PetscCall(EG_invEvaluate(edge, vcoords, params, result));
    2402:       }
    2403:       for (d = 0; d < cdim; ++d) vcoords[d] = result[d];
    2404:     } else if (faceID > 0) {
    2405:       /* Snap to FACE at nearest location */
    2406:       double params[2];
    2407:       if (islite) {
    2408:         PetscCall(EGlite_objectBodyTopo(body, FACE, faceID, &face));
    2409:         PetscCall(EGlite_invEvaluate(face, vcoords, params, result));
    2410:       } // Get (x,y,z) of nearest point on FACE
    2411:       else {
    2412:         PetscCall(EG_objectBodyTopo(body, FACE, faceID, &face));
    2413:         PetscCall(EG_invEvaluate(face, vcoords, params, result));
    2414:       }
    2415:       for (d = 0; d < cdim; ++d) vcoords[d] = result[d];
    2416:     }
    2417:   }
    2418:   PetscCall(VecRestoreArrayWrite(coordinates, &coords));
    2419:   /* Clear out global coordinates */
    2420:   PetscCall(VecDestroy(&dm->coordinates[0].x));
    2421: #endif
    2422:   PetscFunctionReturn(PETSC_SUCCESS);
    2423: }

    2425: #if defined(PETSC_HAVE_EGADS)
    2426: // This replaces the model in-place
    2427: PetscErrorCode ConvertGeomModelToAllBSplines(PetscBool islite, ego *model) PeNS
    2428: {
    2429:   /* EGADS/EGADSlite Variables */
    2430:   ego  context = NULL, geom, *bodies, *fobjs;
    2431:   int  oclass, mtype;
    2432:   int *senses;
    2433:   int  Nb, Nf;

    2435:   PetscFunctionBegin;
    2436:   // Get the number of bodies and body objects in the model
    2437:   if (islite) PetscCallEGADS(EGlite_getTopology, (*model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
    2438:   else PetscCallEGADS(EG_getTopology, (*model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));

    2440:   // Get all Faces on the body    <-- Only working with 1 body at the moment.
    2441:   ego body = bodies[0];
    2442:   if (islite) PetscCallEGADS(EGlite_getBodyTopos, (body, NULL, FACE, &Nf, &fobjs));
    2443:   else PetscCallEGADS(EG_getBodyTopos, (body, NULL, FACE, &Nf, &fobjs));
    2444:   ego newGeom[Nf];
    2445:   ego newFaces[Nf];

    2447:   // Convert the 1st Face to a BSpline Geometry
    2448:   for (int ii = 0; ii < Nf; ++ii) {
    2449:     ego     face = fobjs[ii];
    2450:     ego     gRef, gPrev, gNext, *lobjs;
    2451:     int     goclass, gmtype, *gpinfo;
    2452:     int     Nl, *lsenses;
    2453:     double *gprv;
    2454:     char   *gClass = (char *)"", *gType = (char *)"";

    2456:     /* Shape Optimization is NOT available for EGADSlite geometry files. */
    2457:     /*     Note :: islite options are left below in case future versions of EGADSlite includes this capability */
    2458:     PetscCheck(!islite, PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot convert geometric entities to all BSplines for geometries defined by EGADSlite (.egadslite)! Please use another geometry file format STEP, IGES, EGADS or BRep");

    2460:     if (islite) {
    2461:       PetscCallEGADS(EGlite_getTopology, (face, &geom, &oclass, &mtype, NULL, &Nl, &lobjs, &lsenses)); // Get FACES Geometry object (geom_
    2462:       PetscCallEGADS(EGlite_getGeometry, (geom, &goclass, &gmtype, &gRef, &gpinfo, &gprv));            // Get geometry object info
    2463:       PetscCallEGADS(EGlite_getInfo, (geom, &goclass, &gmtype, &gRef, &gPrev, &gNext));
    2464:     } // Get geometry info
    2465:     else {
    2466:       PetscCallEGADS(EG_getTopology, (face, &geom, &oclass, &mtype, NULL, &Nl, &lobjs, &lsenses)); // Get FACES Geometry object (geom_
    2467:       PetscCallEGADS(EG_getGeometry, (geom, &goclass, &gmtype, &gRef, &gpinfo, &gprv));            // Get geometry object info
    2468:       PetscCallEGADS(EG_getInfo, (geom, &goclass, &gmtype, &gRef, &gPrev, &gNext));
    2469:     } // Get geometry info

    2471:     PetscCall(DMPlex_EGADS_GeomDecode_Internal(goclass, gmtype, &gClass, &gType)); // Decode Geometry integers

    2473:     // Convert current FACE to a BSpline Surface
    2474:     ego     bspline;
    2475:     ego     bRef, bPrev, bNext;
    2476:     int     boclass, bmtype, *bpinfo;
    2477:     double *bprv;
    2478:     char   *bClass = (char *)"", *bType = (char *)"";

    2480:     PetscCallEGADS(EG_convertToBSpline, (face, &bspline)); // Does not have an EGlite_ version

    2482:     if (islite) {
    2483:       PetscCallEGADS(EGlite_getGeometry, (bspline, &boclass, &bmtype, &bRef, &bpinfo, &bprv)); // Get geometry object info
    2484:       PetscCallEGADS(EGlite_getInfo, (bspline, &boclass, &bmtype, &bRef, &bPrev, &bNext));
    2485:     } // Get geometry info
    2486:     else {
    2487:       PetscCallEGADS(EG_getGeometry, (bspline, &boclass, &bmtype, &bRef, &bpinfo, &bprv)); // Get geometry object info
    2488:       PetscCallEGADS(EG_getInfo, (bspline, &boclass, &bmtype, &bRef, &bPrev, &bNext));
    2489:     } // Get geometry info

    2491:     PetscCall(DMPlex_EGADS_GeomDecode_Internal(boclass, bmtype, &bClass, &bType)); // Decode Geometry integers

    2493:     // Get Context from FACE
    2494:     context = NULL;
    2495:     PetscCallEGADS(EG_getContext, (face, &context)); // Does not have an EGlite_ version

    2497:     // Silence WARNING Regarding OPENCASCADE 7.5
    2498:     if (islite) PetscCallEGADS(EGlite_setOutLevel, (context, 0));
    2499:     else PetscCallEGADS(EG_setOutLevel, (context, 0));

    2501:     ego newgeom;
    2502:     PetscCallEGADS(EG_makeGeometry, (context, SURFACE, BSPLINE, NULL, bpinfo, bprv, &newgeom)); // Does not have an EGlite_ version

    2504:     PetscCallEGADS(EG_deleteObject, (bspline));

    2506:     // Create new FACE based on new SURFACE geometry
    2507:     double data[4];
    2508:     int    periodic;
    2509:     if (islite) PetscCallEGADS(EGlite_getRange, (newgeom, data, &periodic));
    2510:     else PetscCallEGADS(EG_getRange, (newgeom, data, &periodic));

    2512:     ego newface;
    2513:     PetscCallEGADS(EG_makeFace, (newgeom, SFORWARD, data, &newface)); // Does not have an EGlite_ version
    2514:     //PetscCallEGADS(EG_deleteObject, (newgeom));
    2515:     //PetscCallEGADS(EG_deleteObject, (newface));
    2516:     newFaces[ii] = newface;
    2517:     newGeom[ii]  = newgeom;

    2519:     // Reinstate WARNING Regarding OPENCASCADE 7.5
    2520:     if (islite) PetscCallEGADS(EGlite_setOutLevel, (context, 1));
    2521:     else PetscCallEGADS(EG_setOutLevel, (context, 1));
    2522:   }

    2524:   // Sew New Faces together to get a new model
    2525:   ego newmodel;
    2526:   PetscCallEGADS(EG_sewFaces, (Nf, newFaces, 0.0, 0, &newmodel)); // Does not have an EGlite_ version
    2527:   for (int ii = 0; ii < Nf; ++ii) {
    2528:     PetscCallEGADS(EG_deleteObject, (newFaces[ii]));
    2529:     PetscCallEGADS(EG_deleteObject, (newGeom[ii]));
    2530:   }
    2531:   PetscCallEGADS(EG_deleteObject, (*model));
    2532:   *model = newmodel;
    2533:   PetscFunctionReturn(PETSC_SUCCESS);
    2534: }
    2535: #endif

    2537: /*@C
    2538:   DMPlexCreateGeomFromFile - Create a `DMPLEX` mesh from an EGADS, IGES, or STEP file.

    2540:   Collective

    2542:   Input Parameters:
    2543: + comm     - The MPI communicator
    2544: . filename - The name of the EGADS, IGES, or STEP file
    2545: - islite   - Flag for EGADSlite support

    2547:   Output Parameter:
    2548: . dm - The `DM` object representing the mesh

    2550:   Level: beginner

    2552: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexCreateEGADS()`, `DMPlexCreateEGADSliteFromFile()`
    2553: @*/
    2554: PetscErrorCode DMPlexCreateGeomFromFile(MPI_Comm comm, const char filename[], DM *dm, PetscBool islite) PeNS
    2555: {
    2556:   /* PETSc Variables */
    2557:   PetscMPIInt rank;
    2558:   PetscBool   printModel = PETSC_FALSE, tessModel = PETSC_FALSE, newModel = PETSC_FALSE;
    2559:   PetscBool   shapeOpt = PETSC_FALSE;

    2561: #if defined(PETSC_HAVE_EGADS)
    2562:   ego context = NULL, model = NULL;
    2563: #endif

    2565:   PetscFunctionBegin;
    2566:   PetscAssertPointer(filename, 2);
    2567:   PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_geom_print_model", &printModel, NULL));
    2568:   PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_geom_tess_model", &tessModel, NULL));
    2569:   PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_geom_new_model", &newModel, NULL));
    2570:   PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_geom_shape_opt", &shapeOpt, NULL));
    2571:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
    2572: #if defined(PETSC_HAVE_EGADS)
    2573:   if (rank == 0) {
    2574:     /* EGADSlite files cannot be used for Shape Optimization Work. It lacks the ability to make new geometry. */
    2575:     /* Must use EGADS, STEP, IGES or BRep files to perform this work.                                         */
    2576:     if (islite) {
    2577:       PetscCallEGADS(EGlite_open, (&context));
    2578:       PetscCallEGADS(EGlite_loadModel, (context, 0, filename, &model));
    2579:       if (shapeOpt) PetscCall(ConvertGeomModelToAllBSplines(islite, &model));
    2580:       if (printModel) PetscCall(DMPlexGeomPrintModel_Internal(model, islite));
    2581:     } else {
    2582:       PetscCallEGADS(EG_open, (&context));
    2583:       PetscCallEGADS(EG_loadModel, (context, 0, filename, &model));
    2584:       if (shapeOpt) PetscCall(ConvertGeomModelToAllBSplines(islite, &model));
    2585:       if (printModel) PetscCall(DMPlexGeomPrintModel_Internal(model, islite));
    2586:     }
    2587:   }
    2588:   if (tessModel) PetscCall(DMPlexCreateGeom_Tess_Internal(comm, context, model, dm, islite));
    2589:   else if (newModel) PetscCall(DMPlexCreateGeom_Internal(comm, context, model, dm, islite));
    2590:   else PetscCall(DMPlexCreateGeom(comm, context, model, dm, islite));
    2591:   PetscFunctionReturn(PETSC_SUCCESS);
    2592: #else
    2593:   SETERRQ(comm, PETSC_ERR_SUP, "This method requires EGADS support. Reconfigure using --download-egads");
    2594: #endif
    2595: }

    2597: #if defined(PETSC_HAVE_EGADS)
    2598: /*@C
    2599:   DMPlex_Surface_Grad - Exposes the Geometry's Control Points and Weights and Calculates the Mesh Topology Boundary Nodes Gradient
    2600:                         with respect the associated geometry's Control Points and Weights.

    2602:                         // ----- Depreciated ---- See DMPlexGeomDataAndGrads ------ //

    2604:   Collective

    2606:   Input Parameters:
    2607: . dm      - The DM object representing the mesh with PetscContainer containing an EGADS geometry model

    2609:   Output Parameter:
    2610: . dm       - The DM object representing the mesh with PetscContainers containing the EGADS geometry model, Array-Hash Table Geometry Control Point Pair, Array-Hash Table Geometry Weights Pair and Matrix-Hash Table Surface Gradient Pair

    2612:   Level: intermediate

    2614: .seealso:
    2615: @*/
    2616: PetscErrorCode DMPlex_Surface_Grad(DM dm)
    2617: {
    2618:   ego            model, geom, *bodies, *fobjs;
    2619:   PetscContainer modelObj;
    2620:   int            oclass, mtype, *senses;
    2621:   int            Nb, Nf;
    2622:   PetscHMapI     faceCntrlPtRow_Start = NULL, faceCPWeightsRow_Start = NULL;
    2623:   PetscHMapI     pointSurfGradRow_Start = NULL;
    2624:   Mat            pointSurfGrad;
    2625:   IS             faceLabelValues, edgeLabelValues, vertexLabelValues;
    2626:   PetscInt       faceLabelSize, edgeLabelSize, vertexLabelSize;
    2627:   PetscBool      islite = PETSC_FALSE;

    2629:   PetscFunctionBegin;
    2630:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
    2631:   if (!modelObj) {
    2632:     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
    2633:     islite = PETSC_TRUE;
    2634:     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, " Cannot provide geometric data or associated calculated gradients for geometries defined by EGADSlite (.egadslite)! \n Please use another geometry file format STEP, IGES, EGADS or BRep");
    2635:   }

    2637:   // Get attached EGADS model (pointer)
    2638:   PetscCall(PetscContainerGetPointer(modelObj, &model));

    2640:   // Get the bodies in the model
    2641:   if (islite) PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
    2642:   else PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));

    2644:   ego body = bodies[0]; // Only operate on 1st body. Model should only have 1 body.

    2646:   // Get the total number of FACEs in the model
    2647:   if (islite) PetscCall(EGlite_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
    2648:   else PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));

    2650:   // Get the total number of points and IDs in the DMPlex with a "EGADS Face Label"
    2651:   // This will provide the total number of DMPlex points on the boundary of the geometry
    2652:   PetscCall(DMGetLabelIdIS(dm, "EGADS Face ID", &faceLabelValues));
    2653:   PetscCall(DMGetLabelSize(dm, "EGADS Face ID", &faceLabelSize));

    2655:   PetscCall(DMGetLabelIdIS(dm, "EGADS Edge ID", &edgeLabelValues));
    2656:   PetscCall(DMGetLabelSize(dm, "EGADS Edge ID", &edgeLabelSize));

    2658:   PetscCall(DMGetLabelIdIS(dm, "EGADS Vertex ID", &vertexLabelValues));
    2659:   PetscCall(DMGetLabelSize(dm, "EGADS Vertex ID", &vertexLabelSize));

    2661:   const PetscInt *faceIndices, *edgeIndices, *vertexIndices;
    2662:   PetscCall(ISGetIndices(faceLabelValues, &faceIndices));
    2663:   PetscCall(ISGetIndices(edgeLabelValues, &edgeIndices));
    2664:   PetscCall(ISGetIndices(vertexLabelValues, &vertexIndices));

    2666:   // Get the points associated with each FACE, EDGE and VERTEX label in the DM
    2667:   PetscInt totalNumPoints = 0;
    2668:   for (int ii = 0; ii < faceLabelSize; ++ii) {
    2669:     // Cycle through FACE labels
    2670:     PetscInt size;
    2671:     PetscCall(DMGetStratumSize(dm, "EGADS Face ID", faceIndices[ii], &size));
    2672:     totalNumPoints += size;
    2673:   }
    2674:   PetscCall(ISRestoreIndices(faceLabelValues, &faceIndices));
    2675:   PetscCall(ISDestroy(&faceLabelValues));

    2677:   for (int ii = 0; ii < edgeLabelSize; ++ii) {
    2678:     // Cycle Through EDGE Labels
    2679:     PetscInt size;
    2680:     PetscCall(DMGetStratumSize(dm, "EGADS Edge ID", edgeIndices[ii], &size));
    2681:     totalNumPoints += size;
    2682:   }
    2683:   PetscCall(ISRestoreIndices(edgeLabelValues, &edgeIndices));
    2684:   PetscCall(ISDestroy(&edgeLabelValues));

    2686:   for (int ii = 0; ii < vertexLabelSize; ++ii) {
    2687:     // Cycle Through VERTEX Labels
    2688:     PetscInt size;
    2689:     PetscCall(DMGetStratumSize(dm, "EGADS Vertex ID", vertexIndices[ii], &size));
    2690:     totalNumPoints += size;
    2691:   }
    2692:   PetscCall(ISRestoreIndices(vertexLabelValues, &vertexIndices));
    2693:   PetscCall(ISDestroy(&vertexLabelValues));

    2695:   int     maxNumCPs   = 0;
    2696:   int     totalNumCPs = 0;
    2697:   ego     bRef, bPrev, bNext, fgeom, *lobjs;
    2698:   int     id, boclass, bmtype, *bpinfo;
    2699:   int     foclass, fmtype, Nl, *lsenses;
    2700:   double *bprv;
    2701:   double  fdata[4];

    2703:   // Create Hash Tables
    2704:   PetscInt cntr = 0, wcntr = 0;
    2705:   PetscCall(PetscHMapICreate(&faceCntrlPtRow_Start));
    2706:   PetscCall(PetscHMapICreate(&faceCPWeightsRow_Start));

    2708:   for (int ii = 0; ii < Nf; ++ii) {
    2709:     // Need to get the maximum number of Control Points defining the FACEs
    2710:     ego face = fobjs[ii];
    2711:     int maxNumCPs_temp;

    2713:     if (islite) {
    2714:       id = EGlite_indexBodyTopo(body, face);
    2715:       PetscCall(EGlite_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
    2716:       PetscCall(EGlite_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
    2717:       PetscCall(EGlite_getInfo(fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
    2718:     } else {
    2719:       id = EG_indexBodyTopo(body, face);
    2720:       PetscCall(EG_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
    2721:       PetscCall(EG_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
    2722:       PetscCall(EG_getInfo(fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
    2723:     }

    2725:     maxNumCPs_temp = bpinfo[2] * bpinfo[5];
    2726:     totalNumCPs += bpinfo[2] * bpinfo[5];

    2728:     if (maxNumCPs_temp > maxNumCPs) maxNumCPs = maxNumCPs_temp;
    2729:   }

    2731:   PetscInt *cpCoordDataLengthPtr, *wDataLengthPtr;
    2732:   PetscInt  cpCoordDataLength = 3 * totalNumCPs;
    2733:   PetscInt  wDataLength       = totalNumCPs;
    2734:   cpCoordDataLengthPtr        = &cpCoordDataLength;
    2735:   wDataLengthPtr              = &wDataLength;
    2736:   PetscScalar *cntrlPtCoords, *cntrlPtWeights;
    2737:   PetscMalloc1(cpCoordDataLength, &cntrlPtCoords);
    2738:   PetscMalloc1(wDataLength, &cntrlPtWeights);
    2739:   for (int ii = 0; ii < Nf; ++ii) {
    2740:     // Need to Populate Control Point Coordinates and Weight Vectors
    2741:     ego           face = fobjs[ii];
    2742:     PetscHashIter hashKeyIter, wHashKeyIter;
    2743:     PetscBool     hashKeyFound, wHashKeyFound;

    2745:     if (islite) {
    2746:       id = EGlite_indexBodyTopo(body, face);
    2747:       PetscCall(EGlite_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
    2748:       PetscCall(EGlite_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
    2749:       PetscCall(EGlite_getInfo(fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
    2750:     } else {
    2751:       id = EG_indexBodyTopo(body, face);
    2752:       PetscCall(EG_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
    2753:       PetscCall(EG_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
    2754:       PetscCall(EG_getInfo(fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
    2755:     }

    2757:     // Store Face ID to 1st Row of Control Point Vector
    2758:     PetscCall(PetscHMapIFind(faceCntrlPtRow_Start, id, &hashKeyIter, &hashKeyFound));

    2760:     if (!hashKeyFound) PetscCall(PetscHMapISet(faceCntrlPtRow_Start, id, cntr));

    2762:     int offsetCoord = bpinfo[3] + bpinfo[6];
    2763:     for (int jj = 0; jj < 3 * bpinfo[2] * bpinfo[5]; ++jj) {
    2764:       cntrlPtCoords[cntr] = bprv[offsetCoord + jj];
    2765:       cntr += 1;
    2766:     }

    2768:     // Store Face ID to 1st Row of Control Point Weight Vector
    2769:     PetscCall(PetscHMapIFind(faceCPWeightsRow_Start, id, &wHashKeyIter, &wHashKeyFound));

    2771:     if (!wHashKeyFound) PetscCall(PetscHMapISet(faceCPWeightsRow_Start, id, wcntr));

    2773:     int offsetWeight = bpinfo[3] + bpinfo[6] + (3 * bpinfo[2] * bpinfo[5]);
    2774:     for (int jj = 0; jj < bpinfo[2] * bpinfo[5]; ++jj) {
    2775:       cntrlPtWeights[wcntr] = bprv[offsetWeight + jj];
    2776:       wcntr += 1;
    2777:     }
    2778:   }

    2780:   // Attach Control Point and Weight Data to DM
    2781:   {
    2782:     PetscContainer cpOrgObj, cpCoordObj, cpCoordLengthObj;
    2783:     PetscContainer wOrgObj, wValObj, wDataLengthObj;

    2785:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &cpOrgObj));
    2786:     PetscCall(PetscContainerSetPointer(cpOrgObj, faceCntrlPtRow_Start));
    2787:     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Hash Table", (PetscObject)cpOrgObj));
    2788:     PetscCall(PetscContainerDestroy(&cpOrgObj));

    2790:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &cpCoordObj));
    2791:     PetscCall(PetscContainerSetPointer(cpCoordObj, cntrlPtCoords));
    2792:     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Coordinates", (PetscObject)cpCoordObj));
    2793:     PetscCall(PetscContainerDestroy(&cpCoordObj));

    2795:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &cpCoordLengthObj));
    2796:     PetscCall(PetscContainerSetPointer(cpCoordLengthObj, cpCoordDataLengthPtr));
    2797:     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Coordinate Data Length", (PetscObject)cpCoordLengthObj));
    2798:     PetscCall(PetscContainerDestroy(&cpCoordLengthObj));

    2800:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &wOrgObj));
    2801:     PetscCall(PetscContainerSetPointer(wOrgObj, faceCPWeightsRow_Start));
    2802:     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weights Hash Table", (PetscObject)wOrgObj));
    2803:     PetscCall(PetscContainerDestroy(&wOrgObj));

    2805:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &wValObj));
    2806:     PetscCall(PetscContainerSetPointer(wValObj, cntrlPtWeights));
    2807:     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weight Data", (PetscObject)wValObj));
    2808:     PetscCall(PetscContainerDestroy(&wValObj));

    2810:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &wDataLengthObj));
    2811:     PetscCall(PetscContainerSetPointer(wDataLengthObj, wDataLengthPtr));
    2812:     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weight Data Length", (PetscObject)wDataLengthObj));
    2813:     PetscCall(PetscContainerDestroy(&wDataLengthObj));
    2814:   }

    2816:   // Define Matrix to store  Surface Gradient information dx_i/dCPj_i
    2817:   PetscInt       gcntr   = 0;
    2818:   const PetscInt rowSize = 3 * maxNumCPs * totalNumPoints;
    2819:   const PetscInt colSize = 4 * Nf;

    2821:   // Create Point Surface Gradient Matrix
    2822:   MatCreate(PETSC_COMM_WORLD, &pointSurfGrad);
    2823:   MatSetSizes(pointSurfGrad, PETSC_DECIDE, PETSC_DECIDE, rowSize, colSize);
    2824:   MatSetType(pointSurfGrad, MATAIJ);
    2825:   MatSetUp(pointSurfGrad);

    2827:   // Create Hash Table to store Point's stare row in surfaceGrad[][]
    2828:   PetscCall(PetscHMapICreate(&pointSurfGradRow_Start));

    2830:   // Get Coordinates for the DMPlex point
    2831:   DM           cdm;
    2832:   PetscInt     dE, Nv;
    2833:   Vec          coordinatesLocal;
    2834:   PetscScalar *coords = NULL;
    2835:   PetscCall(DMGetCoordinateDM(dm, &cdm));
    2836:   PetscCall(DMGetCoordinateDim(dm, &dE));
    2837:   PetscCall(DMGetCoordinatesLocal(dm, &coordinatesLocal));

    2839:   // CYCLE THROUGH FACEs
    2840:   for (int ii = 0; ii < Nf; ++ii) {
    2841:     ego             face = fobjs[ii];
    2842:     ego            *eobjs, *nobjs;
    2843:     PetscInt        fid, Ne, Nn;
    2844:     DMLabel         faceLabel, edgeLabel, nodeLabel;
    2845:     PetscHMapI      currFaceUniquePoints = NULL;
    2846:     IS              facePoints, edgePoints, nodePoints;
    2847:     const PetscInt *fIndices, *eIndices, *nIndices;
    2848:     PetscInt        fSize, eSize, nSize;
    2849:     PetscHashIter   fHashKeyIter, eHashKeyIter, nHashKeyIter, pHashKeyIter;
    2850:     PetscBool       fHashKeyFound, eHashKeyFound, nHashKeyFound, pHashKeyFound;
    2851:     PetscInt        cfCntr = 0;

    2853:     // Get Geometry Object for the Current FACE
    2854:     if (islite) {
    2855:       PetscCall(EGlite_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
    2856:       PetscCall(EGlite_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
    2857:     } else {
    2858:       PetscCall(EG_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
    2859:       PetscCall(EG_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
    2860:     }

    2862:     // Get all EDGE and NODE objects attached to the current FACE
    2863:     if (islite) {
    2864:       PetscCall(EGlite_getBodyTopos(body, face, EDGE, &Ne, &eobjs));
    2865:       PetscCall(EGlite_getBodyTopos(body, face, NODE, &Nn, &nobjs));
    2866:     } else {
    2867:       PetscCall(EG_getBodyTopos(body, face, EDGE, &Ne, &eobjs));
    2868:       PetscCall(EG_getBodyTopos(body, face, NODE, &Nn, &nobjs));
    2869:     }

    2871:     // Get all DMPlex Points that have DMLabel "EGADS Face ID" and store them in a Hash Table for later use
    2872:     if (islite) {
    2873:       fid = EGlite_indexBodyTopo(body, face);
    2874:     } else {
    2875:       fid = EG_indexBodyTopo(body, face);
    2876:     }

    2878:     PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
    2879:     PetscCall(DMLabelGetStratumIS(faceLabel, fid, &facePoints));
    2880:     PetscCall(ISGetIndices(facePoints, &fIndices));
    2881:     PetscCall(ISGetSize(facePoints, &fSize));

    2883:     PetscCall(PetscHMapICreate(&currFaceUniquePoints));

    2885:     for (int jj = 0; jj < fSize; ++jj) {
    2886:       PetscCall(PetscHMapIFind(currFaceUniquePoints, fIndices[jj], &fHashKeyIter, &fHashKeyFound));

    2888:       if (!fHashKeyFound) {
    2889:         PetscCall(PetscHMapISet(currFaceUniquePoints, fIndices[jj], cfCntr));
    2890:         cfCntr += 1;
    2891:       }

    2893:       PetscCall(PetscHMapIFind(pointSurfGradRow_Start, fIndices[jj], &pHashKeyIter, &pHashKeyFound));

    2895:       if (!pHashKeyFound) {
    2896:         PetscCall(PetscHMapISet(pointSurfGradRow_Start, fIndices[jj], gcntr));
    2897:         gcntr += 3 * maxNumCPs;
    2898:       }
    2899:     }
    2900:     PetscCall(ISRestoreIndices(facePoints, &fIndices));
    2901:     PetscCall(ISDestroy(&facePoints));

    2903:     // Get all DMPlex Points that have DMLable "EGADS Edge ID" attached to the current FACE and store them in a Hash Table for later use.
    2904:     for (int jj = 0; jj < Ne; ++jj) {
    2905:       ego       edge = eobjs[jj];
    2906:       PetscBool containLabelValue;

    2908:       if (islite) {
    2909:         id = EGlite_indexBodyTopo(body, edge);
    2910:       } else {
    2911:         id = EG_indexBodyTopo(body, edge);
    2912:       }

    2914:       PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
    2915:       PetscCall(DMLabelHasValue(edgeLabel, id, &containLabelValue));

    2917:       if (containLabelValue) {
    2918:         PetscCall(DMLabelGetStratumIS(edgeLabel, id, &edgePoints));
    2919:         PetscCall(ISGetIndices(edgePoints, &eIndices));
    2920:         PetscCall(ISGetSize(edgePoints, &eSize));

    2922:         for (int kk = 0; kk < eSize; ++kk) {
    2923:           PetscCall(PetscHMapIFind(currFaceUniquePoints, eIndices[kk], &eHashKeyIter, &eHashKeyFound));

    2925:           if (!eHashKeyFound) {
    2926:             PetscCall(PetscHMapISet(currFaceUniquePoints, eIndices[kk], cfCntr));
    2927:             cfCntr += 1;
    2928:           }

    2930:           PetscCall(PetscHMapIFind(pointSurfGradRow_Start, eIndices[kk], &pHashKeyIter, &pHashKeyFound));

    2932:           if (!pHashKeyFound) {
    2933:             PetscCall(PetscHMapISet(pointSurfGradRow_Start, eIndices[kk], gcntr));
    2934:             gcntr += 3 * maxNumCPs;
    2935:           }
    2936:         }
    2937:         PetscCall(ISRestoreIndices(edgePoints, &eIndices));
    2938:         PetscCall(ISDestroy(&edgePoints));
    2939:       }
    2940:     }

    2942:     // Get all DMPlex Points that have DMLabel "EGADS Vertex ID" attached to the current FACE and store them in a Hash Table for later use.
    2943:     for (int jj = 0; jj < Nn; ++jj) {
    2944:       ego node = nobjs[jj];

    2946:       if (islite) {
    2947:         id = EGlite_indexBodyTopo(body, node);
    2948:       } else {
    2949:         id = EG_indexBodyTopo(body, node);
    2950:       }

    2952:       PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &nodeLabel));
    2953:       PetscCall(DMLabelGetStratumIS(nodeLabel, id, &nodePoints));
    2954:       PetscCall(ISGetIndices(nodePoints, &nIndices));
    2955:       PetscCall(ISGetSize(nodePoints, &nSize));

    2957:       for (int kk = 0; kk < nSize; ++kk) {
    2958:         PetscCall(PetscHMapIFind(currFaceUniquePoints, nIndices[kk], &nHashKeyIter, &nHashKeyFound));

    2960:         if (!nHashKeyFound) {
    2961:           PetscCall(PetscHMapISet(currFaceUniquePoints, nIndices[kk], cfCntr));
    2962:           cfCntr += 1;
    2963:         }

    2965:         PetscCall(PetscHMapIFind(pointSurfGradRow_Start, nIndices[kk], &pHashKeyIter, &pHashKeyFound));
    2966:         if (!pHashKeyFound) {
    2967:           PetscCall(PetscHMapISet(pointSurfGradRow_Start, nIndices[kk], gcntr));
    2968:           gcntr += 3 * maxNumCPs;
    2969:         }
    2970:       }
    2971:       PetscCall(ISRestoreIndices(nodePoints, &nIndices));
    2972:       PetscCall(ISDestroy(&nodePoints));
    2973:     }

    2975:     // Get the Total Number of entries in the Hash Table
    2976:     PetscInt currFaceUPSize;
    2977:     PetscCall(PetscHMapIGetSize(currFaceUniquePoints, &currFaceUPSize));

    2979:     // Get Keys
    2980:     PetscInt currFaceUPKeys[currFaceUPSize], off = 0;
    2981:     PetscCall(PetscHMapIGetKeys(currFaceUniquePoints, &off, currFaceUPKeys));

    2983:     // Cycle through all points on the current FACE
    2984:     for (int jj = 0; jj < currFaceUPSize; ++jj) {
    2985:       PetscInt currPointID = currFaceUPKeys[jj];
    2986:       PetscCall(DMPlexVecGetClosure(cdm, NULL, coordinatesLocal, currPointID, &Nv, &coords));

    2988:       // Get UV position of FACE
    2989:       double params[2], range[4], eval[18];
    2990:       int    peri;

    2992:       if (islite) {
    2993:         PetscCall(EGlite_getRange(face, range, &peri));
    2994:       } else {
    2995:         PetscCall(EG_getRange(face, range, &peri));
    2996:       }

    2998:       PetscCall(DMPlex_Geom_FACE_XYZtoUV_Internal(coords, face, range, 0, dE, params, islite));

    3000:       if (islite) {
    3001:         PetscCall(EGlite_evaluate(face, params, eval));
    3002:       } else {
    3003:         PetscCall(EG_evaluate(face, params, eval));
    3004:       }

    3006:       // Make a new SURFACE Geometry by changing the location of the Control Points
    3007:       int    prvSize = bpinfo[3] + bpinfo[6] + (4 * bpinfo[2] * bpinfo[5]);
    3008:       double nbprv[prvSize];

    3010:       // Cycle through each Control Point
    3011:       double deltaCoord = 1.0E-4;
    3012:       int    offset     = bpinfo[3] + bpinfo[6];
    3013:       int    wOffset    = offset + (3 * bpinfo[2] * bpinfo[5]);
    3014:       for (int ii = 0; ii < bpinfo[2] * bpinfo[5]; ++ii) {
    3015:         // Cycle through each direction (x, then y, then z)
    3016:         for (int kk = 0; kk < 4; ++kk) {
    3017:           // Reinitialize nbprv[] values because we only want to change one value at a time
    3018:           for (int mm = 0; mm < prvSize; ++mm) nbprv[mm] = bprv[mm];

    3020:           if (kk == 0) { //X
    3021:             nbprv[offset + 0] = bprv[offset + 0] + deltaCoord;
    3022:             nbprv[offset + 1] = bprv[offset + 1];
    3023:             nbprv[offset + 2] = bprv[offset + 2];
    3024:           } else if (kk == 1) { //Y
    3025:             nbprv[offset + 0] = bprv[offset + 0];
    3026:             nbprv[offset + 1] = bprv[offset + 1] + deltaCoord;
    3027:             nbprv[offset + 2] = bprv[offset + 2];
    3028:           } else if (kk == 2) { //Z
    3029:             nbprv[offset + 0] = bprv[offset + 0];
    3030:             nbprv[offset + 1] = bprv[offset + 1];
    3031:             nbprv[offset + 2] = bprv[offset + 2] + deltaCoord;
    3032:           } else if (kk == 3) { // Weights
    3033:             nbprv[wOffset + ii] = bprv[wOffset + ii] + deltaCoord;
    3034:           } else {
    3035:             // currently do nothing
    3036:           }

    3038:           // Create New Surface Based on New Control Points or Weights
    3039:           ego newgeom, context;
    3040:           if (islite) {
    3041:             PetscCall(EGlite_open(&context));
    3042:             PetscCall(EGlite_setOutLevel(context, 0));
    3043:           } else {
    3044:             PetscCall(EG_open(&context));
    3045:             PetscCall(EG_setOutLevel(context, 0));
    3046:           }

    3048:           PetscCall(EG_makeGeometry(context, SURFACE, BSPLINE, NULL, bpinfo, nbprv, &newgeom)); // Does not have an EGlite_ version KNOWN_ISSUE

    3050:           if (islite) PetscCall(EGlite_setOutLevel(context, 1));
    3051:           else PetscCall(EG_setOutLevel(context, 1));

    3053:           // Evaluate new (x, y, z) Point Position based on new Surface Definition
    3054:           double newCoords[18];
    3055:           if (islite) {
    3056:             PetscCall(EGlite_getRange(newgeom, range, &peri));
    3057:           } else {
    3058:             PetscCall(EG_getRange(newgeom, range, &peri));
    3059:           }

    3061:           PetscCall(DMPlex_Geom_FACE_XYZtoUV_Internal(coords, newgeom, range, 0, dE, params, islite));

    3063:           if (islite) {
    3064:             PetscCall(EGlite_evaluate(newgeom, params, newCoords));
    3065:           } else {
    3066:             PetscCall(EG_evaluate(newgeom, params, newCoords));
    3067:           }

    3069:           // Now Calculate the Surface Gradient for the change in x-component Control Point
    3070:           PetscScalar dxdCx = (newCoords[0] - coords[0]) / deltaCoord;
    3071:           PetscScalar dxdCy = (newCoords[1] - coords[1]) / deltaCoord;
    3072:           PetscScalar dxdCz = (newCoords[2] - coords[2]) / deltaCoord;

    3074:           // Store Gradient Information in surfaceGrad[][] Matrix
    3075:           PetscInt startRow;
    3076:           PetscCall(PetscHMapIGet(pointSurfGradRow_Start, currPointID, &startRow));

    3078:           // Store Results in PETSc Mat
    3079:           PetscCall(MatSetValue(pointSurfGrad, startRow + (ii * 3) + 0, ((fid - 1) * 4) + kk, dxdCx, INSERT_VALUES));
    3080:           PetscCall(MatSetValue(pointSurfGrad, startRow + (ii * 3) + 1, ((fid - 1) * 4) + kk, dxdCy, INSERT_VALUES));
    3081:           PetscCall(MatSetValue(pointSurfGrad, startRow + (ii * 3) + 2, ((fid - 1) * 4) + kk, dxdCz, INSERT_VALUES));
    3082:         }
    3083:         offset += 3;
    3084:       }
    3085:       PetscCall(DMPlexVecRestoreClosure(cdm, NULL, coordinatesLocal, currPointID, &Nv, &coords));
    3086:     }
    3087:   }

    3089:   // Assemble Point Surface Grad Matrix
    3090:   MatAssemblyBegin(pointSurfGrad, MAT_FINAL_ASSEMBLY);
    3091:   MatAssemblyEnd(pointSurfGrad, MAT_FINAL_ASSEMBLY);

    3093:   // Attach Surface Gradient Hash Table and Matrix to DM
    3094:   {
    3095:     PetscContainer surfGradOrgObj, surfGradObj;

    3097:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &surfGradOrgObj));
    3098:     PetscCall(PetscContainerSetPointer(surfGradOrgObj, pointSurfGradRow_Start));
    3099:     PetscCall(PetscObjectCompose((PetscObject)dm, "Surface Gradient Hash Table", (PetscObject)surfGradOrgObj));
    3100:     PetscCall(PetscContainerDestroy(&surfGradOrgObj));

    3102:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &surfGradObj));
    3103:     PetscCall(PetscContainerSetPointer(surfGradObj, pointSurfGrad));
    3104:     PetscCall(PetscObjectCompose((PetscObject)dm, "Surface Gradient Matrix", (PetscObject)surfGradObj));
    3105:     PetscCall(PetscContainerDestroy(&surfGradObj));
    3106:   }
    3107:   if (islite) EGlite_free(fobjs);
    3108:   else EG_free(fobjs);
    3109:   PetscFunctionReturn(PETSC_SUCCESS);
    3110: }

    3112: static PetscErrorCode DestroyHashMap(PetscCtxRt p)
    3113: {
    3114:   PetscFunctionBegin;
    3115:   PetscCall(PetscHMapIDestroy((PetscHMapI *)p));
    3116:   PetscFunctionReturn(PETSC_SUCCESS);
    3117: }
    3118: #endif

    3120: /*@C
    3121:   DMPlexGeomDataAndGrads - Exposes Control Points and Control Point Weights defining the underlying geometry allowing user manipulation of the geometry.

    3123:   Collective

    3125:   Input Parameters:
    3126: + dm           - The DM object representing the mesh with PetscContainer containing an EGADS geometry model
    3127: - fullGeomGrad - PetscBool flag. Determines how the Surface Area and Volume Gradients wrt to Control Points and Control Point Weights are calculated.
    3128:                       PETSC_FALSE :: Surface Area Gradient wrt Control Points and Control Point Weights are calculated using the change in the local
    3129:                                      FACE changes (not the entire body). Volume Gradients are not calculated. Faster computations.
    3130:                       PETSC_TRUE  :: Surface Area Gradietn wrt to Control Points and Control Point Weights are calculated using the change observed in
    3131:                                      the entire solid body. Volume Gradients are calculated. Slower computation due to the need to generate a new solid
    3132:                                      body geometry for every Control Point and Control Point Weight change.

    3134:   Output Parameter:
    3135: . dm - The updated DM object representing the mesh with PetscContainers containing the Control Point, Control Point Weight and Gradient Data.

    3137:   Level: intermediate

    3139:   Note:
    3140:   Calculates the DM Point location, surface area and volume gradients wrt to Control Point and Control Point Weights using Finite Difference (small perturbation of Control Point coordinates or Control Point Weight value).

    3142: .seealso: `DMPLEX`, `DMCreate()`, `DMPlexCreateGeom()`, `DMPlexModifyEGADSGeomModel()`
    3143: @*/
    3144: PetscErrorCode DMPlexGeomDataAndGrads(DM dm, PetscBool fullGeomGrad) PeNS
    3145: {
    3146: #if defined(PETSC_HAVE_EGADS)
    3147:   /* PETSc Variables */
    3148:   PetscContainer modelObj;
    3149:   PetscHMapI     faceCntrlPtRow_Start = NULL, faceCPWeightsRow_Start = NULL;
    3150:   PetscHMapI     pointSurfGradRow_Start = NULL;
    3151:   Mat            pointSurfGrad, cpEquiv;
    3152:   IS             faceLabelValues, edgeLabelValues, vertexLabelValues;
    3153:   PetscInt       faceLabelSize, edgeLabelSize, vertexLabelSize;
    3154:   PetscBool      islite = PETSC_FALSE;
    3155:   /* EGADS Variables */
    3156:   ego model, geom, *bodies, *fobjs = NULL;
    3157:   int oclass, mtype, *senses;
    3158:   int Nb, Nf;
    3159: #endif

    3161:   PetscFunctionBegin;
    3162: #if defined(PETSC_HAVE_EGADS)

    3164:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
    3165:   if (!modelObj) {
    3166:     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
    3167:     PetscCheck(modelObj, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Input DM must have attached EGADS Geometry Model");
    3168:     islite = PETSC_TRUE;
    3169:     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot provide geometric data or associated calculated gradients for geometries defined by EGADSlite (.egadslite)!\nPlease use another geometry file format STEP, IGES, EGADS or BRep");
    3170:   }

    3172:   // Get attached EGADS model (pointer)
    3173:   PetscCall(PetscContainerGetPointer(modelObj, &model));

    3175:   // Get the bodies in the model
    3176:   if (islite) PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
    3177:   else PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));

    3179:   ego body = bodies[0]; // Only operate on 1st body. Model should only have 1 body.

    3181:   // Get the total number of FACEs in the model
    3182:   if (islite) PetscCall(EGlite_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
    3183:   else PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));

    3185:   // Get the total number of points and IDs in the DMPlex with a "EGADS Face Label"
    3186:   // This will provide the total number of DMPlex points on the boundary of the geometry
    3187:   PetscCall(DMGetLabelIdIS(dm, "EGADS Face ID", &faceLabelValues));
    3188:   PetscCall(DMGetLabelSize(dm, "EGADS Face ID", &faceLabelSize));

    3190:   PetscCall(DMGetLabelIdIS(dm, "EGADS Edge ID", &edgeLabelValues));
    3191:   PetscCall(DMGetLabelSize(dm, "EGADS Edge ID", &edgeLabelSize));

    3193:   PetscCall(DMGetLabelIdIS(dm, "EGADS Vertex ID", &vertexLabelValues));
    3194:   PetscCall(DMGetLabelSize(dm, "EGADS Vertex ID", &vertexLabelSize));

    3196:   const PetscInt *faceIndices, *edgeIndices, *vertexIndices;
    3197:   PetscCall(ISGetIndices(faceLabelValues, &faceIndices));
    3198:   PetscCall(ISGetIndices(edgeLabelValues, &edgeIndices));
    3199:   PetscCall(ISGetIndices(vertexLabelValues, &vertexIndices));

    3201:   // Get the points associated with each FACE, EDGE and VERTEX label in the DM
    3202:   PetscInt totalNumPoints = 0;
    3203:   for (int f = 0; f < faceLabelSize; ++f) {
    3204:     // Cycle through FACE labels
    3205:     PetscInt size;
    3206:     PetscCall(DMGetStratumSize(dm, "EGADS Face ID", faceIndices[f], &size));
    3207:     totalNumPoints += size;
    3208:   }
    3209:   PetscCall(ISRestoreIndices(faceLabelValues, &faceIndices));
    3210:   PetscCall(ISDestroy(&faceLabelValues));

    3212:   for (int e = 0; e < edgeLabelSize; ++e) {
    3213:     // Cycle Through EDGE Labels
    3214:     PetscInt size;
    3215:     PetscCall(DMGetStratumSize(dm, "EGADS Edge ID", edgeIndices[e], &size));
    3216:     totalNumPoints += size;
    3217:   }
    3218:   PetscCall(ISRestoreIndices(edgeLabelValues, &edgeIndices));
    3219:   PetscCall(ISDestroy(&edgeLabelValues));

    3221:   for (int ii = 0; ii < vertexLabelSize; ++ii) {
    3222:     // Cycle Through VERTEX Labels
    3223:     PetscInt size;
    3224:     PetscCall(DMGetStratumSize(dm, "EGADS Vertex ID", vertexIndices[ii], &size));
    3225:     totalNumPoints += size;
    3226:   }
    3227:   PetscCall(ISRestoreIndices(vertexLabelValues, &vertexIndices));
    3228:   PetscCall(ISDestroy(&vertexLabelValues));

    3230:   int     maxNumCPs   = 0;
    3231:   int     totalNumCPs = 0;
    3232:   ego     bRef, bPrev, bNext, fgeom, *lobjs;
    3233:   int     id, boclass, bmtype, *bpinfo;
    3234:   int     foclass, fmtype, Nl, *lsenses;
    3235:   double *bprv;
    3236:   double  fdata[4];

    3238:   // Create Hash Tables
    3239:   PetscInt cntr = 0, wcntr = 0, vcntr = 0;
    3240:   PetscCall(PetscHMapICreate(&faceCntrlPtRow_Start));
    3241:   PetscCall(PetscHMapICreate(&faceCPWeightsRow_Start));

    3243:   for (int f = 0; f < Nf; ++f) {
    3244:     // Need to get the maximum number of Control Points defining the FACEs
    3245:     ego face = fobjs[f];
    3246:     int maxNumCPs_temp;

    3248:     if (islite) {
    3249:       id = EGlite_indexBodyTopo(body, face);
    3250:       PetscCall(EGlite_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
    3251:       PetscCall(EGlite_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
    3252:       PetscCall(EGlite_getInfo(fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
    3253:     } else {
    3254:       id = EG_indexBodyTopo(body, face);
    3255:       PetscCall(EG_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
    3256:       PetscCall(EG_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
    3257:       PetscCall(EG_getInfo(fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
    3258:     }
    3259:     maxNumCPs_temp = bpinfo[2] * bpinfo[5];
    3260:     totalNumCPs += bpinfo[2] * bpinfo[5];

    3262:     if (maxNumCPs_temp > maxNumCPs) maxNumCPs = maxNumCPs_temp;
    3263:   }

    3265:   PetscInt *cpCoordDataLengthPtr, *wDataLengthPtr;
    3266:   PetscInt  cpCoordDataLength = 3 * totalNumCPs;
    3267:   PetscInt  wDataLength       = totalNumCPs;
    3268:   cpCoordDataLengthPtr        = &cpCoordDataLength;
    3269:   wDataLengthPtr              = &wDataLength;

    3271:   Vec          cntrlPtCoordsVec, cntrlPtWeightsVec;
    3272:   PetscScalar *cntrlPtCoords, *cntrlPtWeights;
    3273:   PetscCall(VecCreateSeq(PETSC_COMM_SELF, cpCoordDataLength, &cntrlPtCoordsVec));
    3274:   PetscCall(VecCreateSeq(PETSC_COMM_SELF, wDataLength, &cntrlPtWeightsVec));

    3276:   // For dSA/dCPi
    3277:   Vec          gradSACPVec, gradSAWVec, gradVCPVec, gradVWVec;
    3278:   PetscScalar *gradSACP, *gradSAW, *gradVCP, *gradVW;
    3279:   PetscCall(VecCreateSeq(PETSC_COMM_SELF, cpCoordDataLength, &gradSACPVec));
    3280:   PetscCall(VecCreateSeq(PETSC_COMM_SELF, wDataLength, &gradSAWVec));
    3281:   PetscCall(VecCreateSeq(PETSC_COMM_SELF, cpCoordDataLength, &gradVCPVec));
    3282:   PetscCall(VecCreateSeq(PETSC_COMM_SELF, wDataLength, &gradVWVec));

    3284:   // Control Point - Vertex/Edge/Face Relationship
    3285:   PetscInt *cp_vertex, *cp_edge, *cp_face;
    3286:   PetscInt *w_vertex, *w_edge, *w_face;
    3287:   PetscCall(PetscMalloc1(totalNumCPs, &cp_vertex));
    3288:   PetscCall(PetscMalloc1(totalNumCPs, &cp_edge));
    3289:   PetscCall(PetscMalloc1(totalNumCPs, &cp_face));
    3290:   PetscCall(PetscMalloc1(wDataLength, &w_vertex));
    3291:   PetscCall(PetscMalloc1(wDataLength, &w_edge));
    3292:   PetscCall(PetscMalloc1(wDataLength, &w_face));

    3294:   for (int f = 0; f < Nf; ++f) {
    3295:     // Need to Populate Control Point Coordinates and Weight Vectors
    3296:     ego           face = fobjs[f];
    3297:     ego          *vobjs, *eobjs;
    3298:     int           offsetCoord, offsetWeight;
    3299:     PetscInt      Nv, Ne, wRowStart = 0;
    3300:     PetscHashIter hashKeyIter, wHashKeyIter;
    3301:     PetscBool     hashKeyFound, wHashKeyFound;

    3303:     if (islite) {
    3304:       id = EGlite_indexBodyTopo(body, face);
    3305:       PetscCallEGADS(EGlite_getTopology, (face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
    3306:       PetscCallEGADS(EGlite_getGeometry, (fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
    3307:       PetscCallEGADS(EGlite_getInfo, (fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
    3308:       PetscCallEGADS(EGlite_getBodyTopos, (body, face, NODE, &Nv, &vobjs));
    3309:     } else {
    3310:       id = EG_indexBodyTopo(body, face);
    3311:       PetscCallEGADS(EG_getTopology, (face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
    3312:       PetscCallEGADS(EG_getGeometry, (fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
    3313:       PetscCallEGADS(EG_getInfo, (fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
    3314:       PetscCallEGADS(EG_getBodyTopos, (body, face, NODE, &Nv, &vobjs));
    3315:     }

    3317:     // Store Face ID to 1st Row of Control Point Vector
    3318:     PetscCall(PetscHMapIFind(faceCntrlPtRow_Start, id, &hashKeyIter, &hashKeyFound));

    3320:     if (!hashKeyFound) PetscCall(PetscHMapISet(faceCntrlPtRow_Start, id, cntr));

    3322:     PetscCall(VecGetArrayWrite(cntrlPtCoordsVec, &cntrlPtCoords));
    3323:     offsetCoord = bpinfo[3] + bpinfo[6];
    3324:     for (int jj = 0; jj < 3 * bpinfo[2] * bpinfo[5]; ++jj) {
    3325:       cntrlPtCoords[cntr] = bprv[offsetCoord + jj];
    3326:       cntr += 1;
    3327:     }

    3329:     // Store Face ID to 1st Row of Control Point Weight Vector
    3330:     PetscCall(PetscHMapIFind(faceCPWeightsRow_Start, id, &wHashKeyIter, &wHashKeyFound));

    3332:     if (!wHashKeyFound) {
    3333:       PetscCall(PetscHMapISet(faceCPWeightsRow_Start, id, wcntr));
    3334:       wRowStart = wcntr;
    3335:     }

    3337:     PetscCall(VecGetArrayWrite(cntrlPtWeightsVec, &cntrlPtWeights));
    3338:     offsetWeight = bpinfo[3] + bpinfo[6] + (3 * bpinfo[2] * bpinfo[5]);
    3339:     for (int jj = 0; jj < bpinfo[2] * bpinfo[5]; ++jj) {
    3340:       cntrlPtWeights[wcntr] = bprv[offsetWeight + jj];
    3341:       cp_face[wcntr]        = id;
    3342:       w_face[wcntr]         = id;
    3343:       wcntr += 1;
    3344:     }
    3345:     PetscCall(VecRestoreArrayWrite(cntrlPtWeightsVec, &cntrlPtWeights));

    3347:     // Associate Control Points with Vertex IDs
    3348:     PetscScalar xcp, ycp, zcp;
    3349:     offsetCoord = bpinfo[3] + bpinfo[6];
    3350:     for (int jj = 0; jj < 3 * bpinfo[2] * bpinfo[5]; jj += 3) {
    3351:       xcp = bprv[offsetCoord + jj + 0];
    3352:       ycp = bprv[offsetCoord + jj + 1];
    3353:       zcp = bprv[offsetCoord + jj + 2];

    3355:       //Initialize Control Point and Weight to Vertex ID relationship to -1
    3356:       cp_vertex[vcntr] = -1;
    3357:       w_vertex[vcntr]  = -1;
    3358:       cp_edge[vcntr]   = -1;
    3359:       w_edge[vcntr]    = -1;

    3361:       for (int kk = 0; kk < Nv; ++kk) {
    3362:         int         vid;
    3363:         double      vCoords[3];
    3364:         PetscScalar vDelta;
    3365:         ego         vertex = vobjs[kk];

    3367:         if (islite) {
    3368:           vid = EGlite_indexBodyTopo(body, vertex);
    3369:           PetscCallEGADS(EGlite_evaluate, (vertex, NULL, vCoords));
    3370:         } else {
    3371:           vid = EG_indexBodyTopo(body, vertex);
    3372:           PetscCallEGADS(EG_evaluate, (vertex, NULL, vCoords));
    3373:         }
    3374:         vDelta = PetscSqrtReal(PetscSqr(vCoords[0] - xcp) + PetscSqr(vCoords[1] - ycp) + PetscSqr(vCoords[2] - zcp));

    3376:         if (vDelta < 1.0E-15) {
    3377:           cp_vertex[vcntr] = vid;
    3378:           w_vertex[vcntr]  = vid;
    3379:         }
    3380:       }
    3381:       vcntr += 1;
    3382:     }
    3383:     // These two line could be replaced with DMPlexFreeGeomObject()
    3384:     if (islite) EGlite_free(vobjs);
    3385:     else EG_free(vobjs);

    3387:     // Associate Control Points with Edge IDs
    3388:     if (islite) PetscCallEGADS(EGlite_getBodyTopos, (body, face, EDGE, &Ne, &eobjs));
    3389:     else PetscCallEGADS(EG_getBodyTopos, (body, face, EDGE, &Ne, &eobjs));

    3391:     int cpV1, cpV2;
    3392:     int minID, maxID;

    3394:     // Along vmin axis
    3395:     minID = wRowStart;
    3396:     maxID = wRowStart + (bpinfo[2] - 1);
    3397:     cpV1  = cp_vertex[minID];
    3398:     cpV2  = cp_vertex[maxID];
    3399:     for (int jj = 0; jj < Ne; ++jj) {
    3400:       ego edge = eobjs[jj];
    3401:       ego egeom, *nobjs;
    3402:       int eoclass, emtype, Nn, *nsenses;
    3403:       int n1ID, n2ID, eid;

    3405:       if (islite) {
    3406:         eid = EGlite_indexBodyTopo(body, edge);
    3407:         PetscCallEGADS(EGlite_getTopology, (edge, &egeom, &eoclass, &emtype, NULL, &Nn, &nobjs, &nsenses));
    3408:       } else {
    3409:         eid = EG_indexBodyTopo(body, edge);
    3410:         PetscCallEGADS(EG_getTopology, (edge, &egeom, &eoclass, &emtype, NULL, &Nn, &nobjs, &nsenses));
    3411:       }

    3413:       if (emtype != DEGENERATE) {
    3414:         // Get IDs for current Edge's End Vertices
    3415:         if (islite) {
    3416:           n1ID = EGlite_indexBodyTopo(body, nobjs[0]);
    3417:           n2ID = EGlite_indexBodyTopo(body, nobjs[1]);
    3418:         } else {
    3419:           n1ID = EG_indexBodyTopo(body, nobjs[0]);
    3420:           n2ID = EG_indexBodyTopo(body, nobjs[1]);
    3421:         }

    3423:         if ((cpV1 == n1ID || cpV1 == n2ID) && (cpV2 == n1ID || cpV2 == n2ID)) {
    3424:           for (int kk = minID + 1; kk < maxID; ++kk) {
    3425:             cp_edge[kk] = eid;
    3426:             w_edge[kk]  = eid;
    3427:           }
    3428:         }
    3429:       }
    3430:     }

    3432:     // Along vmax axis
    3433:     minID = wRowStart + (bpinfo[2] * (bpinfo[5] - 1));
    3434:     maxID = wRowStart + (bpinfo[2] * bpinfo[5] - 1);

    3436:     cpV1 = cp_vertex[minID];
    3437:     cpV2 = cp_vertex[maxID];
    3438:     for (int jj = 0; jj < Ne; ++jj) {
    3439:       ego edge = eobjs[jj];
    3440:       ego egeom, *nobjs;
    3441:       int eoclass, emtype, Nn, *nsenses;
    3442:       int n1ID, n2ID, eid;

    3444:       if (islite) {
    3445:         eid = EGlite_indexBodyTopo(body, edge);
    3446:         PetscCallEGADS(EGlite_getTopology, (edge, &egeom, &eoclass, &emtype, NULL, &Nn, &nobjs, &nsenses));
    3447:       } else {
    3448:         eid = EG_indexBodyTopo(body, edge);
    3449:         PetscCallEGADS(EG_getTopology, (edge, &egeom, &eoclass, &emtype, NULL, &Nn, &nobjs, &nsenses));
    3450:       }

    3452:       if (emtype != DEGENERATE) {
    3453:         // Get IDs for current Edge's End Vertices
    3454:         if (islite) {
    3455:           n1ID = EGlite_indexBodyTopo(body, nobjs[0]);
    3456:           n2ID = EGlite_indexBodyTopo(body, nobjs[1]);
    3457:         } else {
    3458:           n1ID = EG_indexBodyTopo(body, nobjs[0]);
    3459:           n2ID = EG_indexBodyTopo(body, nobjs[1]);
    3460:         }

    3462:         if ((cpV1 == n1ID || cpV1 == n2ID) && (cpV2 == n1ID || cpV2 == n2ID)) {
    3463:           for (int kk = minID + 1; kk < maxID - 1; ++kk) {
    3464:             cp_edge[kk] = eid;
    3465:             w_edge[kk]  = eid;
    3466:           }
    3467:         }
    3468:       }
    3469:     }

    3471:     // Along umin axis
    3472:     minID = wRowStart;
    3473:     maxID = wRowStart + (bpinfo[2] * (bpinfo[5] - 1));

    3475:     cpV1 = cp_vertex[minID];
    3476:     cpV2 = cp_vertex[maxID];
    3477:     for (int jj = 0; jj < Ne; ++jj) {
    3478:       ego edge = eobjs[jj];
    3479:       ego egeom, *nobjs;
    3480:       int eoclass, emtype, Nn, *nsenses;
    3481:       int n1ID, n2ID, eid;

    3483:       if (islite) {
    3484:         eid = EGlite_indexBodyTopo(body, edge);
    3485:         PetscCallEGADS(EGlite_getTopology, (edge, &egeom, &eoclass, &emtype, NULL, &Nn, &nobjs, &nsenses));
    3486:       } else {
    3487:         eid = EG_indexBodyTopo(body, edge);
    3488:         PetscCallEGADS(EG_getTopology, (edge, &egeom, &eoclass, &emtype, NULL, &Nn, &nobjs, &nsenses));
    3489:       }

    3491:       if (emtype != DEGENERATE) {
    3492:         // Get IDs for current Edge's End Vertices
    3493:         if (islite) {
    3494:           n1ID = EGlite_indexBodyTopo(body, nobjs[0]);
    3495:           n2ID = EGlite_indexBodyTopo(body, nobjs[1]);
    3496:         } else {
    3497:           n1ID = EG_indexBodyTopo(body, nobjs[0]);
    3498:           n2ID = EG_indexBodyTopo(body, nobjs[1]);
    3499:         }

    3501:         if ((cpV1 == n1ID || cpV1 == n2ID) && (cpV2 == n1ID || cpV2 == n2ID)) {
    3502:           for (int kk = minID + bpinfo[2]; kk < maxID; kk += bpinfo[2]) {
    3503:             cp_edge[kk] = eid;
    3504:             w_edge[kk]  = eid;
    3505:           }
    3506:         }
    3507:       }
    3508:     }

    3510:     // Along umax axis
    3511:     minID = wRowStart + (bpinfo[2] - 1);
    3512:     maxID = wRowStart + (bpinfo[2] * bpinfo[5]) - 1;
    3513:     cpV1  = cp_vertex[minID];
    3514:     cpV2  = cp_vertex[maxID];
    3515:     for (int jj = 0; jj < Ne; ++jj) {
    3516:       ego edge = eobjs[jj];
    3517:       ego egeom, *nobjs;
    3518:       int eoclass, emtype, Nn, *nsenses;
    3519:       int n1ID, n2ID, eid;

    3521:       if (islite) {
    3522:         eid = EGlite_indexBodyTopo(body, edge);
    3523:         PetscCallEGADS(EGlite_getTopology, (edge, &egeom, &eoclass, &emtype, NULL, &Nn, &nobjs, &nsenses));
    3524:       } else {
    3525:         eid = EG_indexBodyTopo(body, edge);
    3526:         PetscCallEGADS(EG_getTopology, (edge, &egeom, &eoclass, &emtype, NULL, &Nn, &nobjs, &nsenses));
    3527:       }

    3529:       if (emtype != DEGENERATE) {
    3530:         // Get IDs for current Edge's End Vertices
    3531:         if (islite) {
    3532:           n1ID = EGlite_indexBodyTopo(body, nobjs[0]);
    3533:           n2ID = EGlite_indexBodyTopo(body, nobjs[1]);
    3534:         } else {
    3535:           n1ID = EG_indexBodyTopo(body, nobjs[0]);
    3536:           n2ID = EG_indexBodyTopo(body, nobjs[1]);
    3537:         }

    3539:         if ((cpV1 == n1ID || cpV1 == n2ID) && (cpV2 == n1ID || cpV2 == n2ID)) {
    3540:           for (int kk = minID + bpinfo[2]; kk < maxID; kk += bpinfo[2]) {
    3541:             cp_edge[kk] = eid;
    3542:             w_edge[kk]  = eid;
    3543:           }
    3544:         }
    3545:       }
    3546:     }
    3547:     // These two lines could be replaced with DMPlexFreeGeomObject()
    3548:     if (islite) EGlite_free(eobjs);
    3549:     else EG_free(eobjs);
    3550:   }

    3552:   // Determine Control Point Equivalence Matrix relating Control Points between Surfaces
    3553:   //     Note: The Weights will also be tied together in the same manner
    3554:   //           Also can use the Weight Hash Table for Row Start ID of each Face
    3555:   const PetscInt cpRowSize = totalNumCPs;
    3556:   const PetscInt cpColSize = cpRowSize;
    3557:   PetscInt      *maxNumRelatePtr;
    3558:   PetscInt       maxNumRelate = 0;

    3560:   // Create Point Surface Gradient Matrix
    3561:   PetscCall(MatCreate(PETSC_COMM_WORLD, &cpEquiv));
    3562:   PetscCall(MatSetSizes(cpEquiv, PETSC_DECIDE, PETSC_DECIDE, cpRowSize, cpColSize));
    3563:   PetscCall(MatSetType(cpEquiv, MATAIJ));
    3564:   PetscCall(MatSetUp(cpEquiv));

    3566:   for (int ii = 0; ii < totalNumCPs; ++ii) {
    3567:     PetscScalar x1, y1, z1;
    3568:     PetscInt    maxRelateTemp = 0;

    3570:     x1 = cntrlPtCoords[(3 * ii) + 0];
    3571:     y1 = cntrlPtCoords[(3 * ii) + 1];
    3572:     z1 = cntrlPtCoords[(3 * ii) + 2];

    3574:     for (int jj = 0; jj < totalNumCPs; ++jj) {
    3575:       PetscScalar x2, y2, z2;
    3576:       PetscScalar cpDelta, eqFactor;
    3577:       x2 = cntrlPtCoords[(3 * jj) + 0];
    3578:       y2 = cntrlPtCoords[(3 * jj) + 1];
    3579:       z2 = cntrlPtCoords[(3 * jj) + 2];

    3581:       cpDelta = PetscSqrtReal(PetscSqr(x2 - x1) + PetscSqr(y2 - y1) + PetscSqr(z2 - z1));
    3582:       if (cpDelta < 1.0E-15) {
    3583:         eqFactor = 1.0;
    3584:         maxRelateTemp += 1;
    3585:       } else {
    3586:         eqFactor = 0.0;
    3587:       }

    3589:       // Store Results in PETSc Mat
    3590:       PetscCall(MatSetValue(cpEquiv, ii, jj, eqFactor, INSERT_VALUES));
    3591:     }
    3592:     if (maxRelateTemp > maxNumRelate) maxNumRelate = maxRelateTemp;
    3593:   }
    3594:   maxNumRelatePtr = &maxNumRelate;
    3595:   PetscCall(VecRestoreArrayWrite(cntrlPtCoordsVec, &cntrlPtCoords));

    3597:   // Assemble Point Surface Grad Matrix
    3598:   PetscCall(MatAssemblyBegin(cpEquiv, MAT_FINAL_ASSEMBLY));
    3599:   PetscCall(MatAssemblyEnd(cpEquiv, MAT_FINAL_ASSEMBLY));

    3601:   // Attach Control Point and Weight Data to DM
    3602:   {
    3603:     PetscContainer cpOrgObj, cpCoordLengthObj;
    3604:     PetscContainer wOrgObj, wDataLengthObj;
    3605:     PetscContainer cp_faceObj, cp_edgeObj, cp_vertexObj;
    3606:     PetscContainer w_faceObj, w_edgeObj, w_vertexObj;
    3607:     PetscContainer maxNumRelateObj;

    3609:     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Hash Table", (PetscObject *)&cpOrgObj));
    3610:     if (!cpOrgObj) {
    3611:       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &cpOrgObj));
    3612:       PetscCall(PetscContainerSetPointer(cpOrgObj, faceCntrlPtRow_Start));
    3613:       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Hash Table", (PetscObject)cpOrgObj));
    3614:       PetscCall(PetscContainerDestroy(&cpOrgObj));
    3615:     } else {
    3616:       PetscCall(PetscContainerSetPointer(cpOrgObj, faceCntrlPtRow_Start));
    3617:     }

    3619:     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Coordinates", (PetscObject)cntrlPtCoordsVec));
    3620:     PetscCall(VecDestroy(&cntrlPtCoordsVec));

    3622:     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Coordinate Data Length", (PetscObject *)&cpCoordLengthObj));
    3623:     if (!cpCoordLengthObj) {
    3624:       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &cpCoordLengthObj));
    3625:       PetscCall(PetscContainerSetPointer(cpCoordLengthObj, cpCoordDataLengthPtr));
    3626:       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Coordinate Data Length", (PetscObject)cpCoordLengthObj));
    3627:       PetscCall(PetscContainerDestroy(&cpCoordLengthObj));
    3628:     } else {
    3629:       PetscCall(PetscContainerSetPointer(cpCoordLengthObj, cpCoordDataLengthPtr));
    3630:     }

    3632:     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weights Hash Table", (PetscObject *)&wOrgObj));
    3633:     if (!wOrgObj) {
    3634:       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &wOrgObj));
    3635:       PetscCall(PetscContainerSetPointer(wOrgObj, faceCPWeightsRow_Start));
    3636:       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weights Hash Table", (PetscObject)wOrgObj));
    3637:       PetscCall(PetscContainerDestroy(&wOrgObj));
    3638:     } else {
    3639:       PetscCall(PetscContainerSetPointer(wOrgObj, faceCPWeightsRow_Start));
    3640:     }

    3642:     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weight Data", (PetscObject)cntrlPtWeightsVec));
    3643:     PetscCall(VecDestroy(&cntrlPtWeightsVec));

    3645:     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight Data Length", (PetscObject *)&wDataLengthObj));
    3646:     if (!wDataLengthObj) {
    3647:       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &wDataLengthObj));
    3648:       PetscCall(PetscContainerSetPointer(wDataLengthObj, wDataLengthPtr));
    3649:       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weight Data Length", (PetscObject)wDataLengthObj));
    3650:       PetscCall(PetscContainerDestroy(&wDataLengthObj));
    3651:     } else {
    3652:       PetscCall(PetscContainerSetPointer(wDataLengthObj, wDataLengthPtr));
    3653:     }

    3655:     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Equivalency Matrix", (PetscObject)cpEquiv));

    3657:     PetscCall(PetscObjectQuery((PetscObject)dm, "Maximum Number Control Point Equivalency", (PetscObject *)&maxNumRelateObj));
    3658:     if (!maxNumRelateObj) {
    3659:       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &maxNumRelateObj));
    3660:       PetscCall(PetscContainerSetPointer(maxNumRelateObj, maxNumRelatePtr));
    3661:       PetscCall(PetscObjectCompose((PetscObject)dm, "Maximum Number Control Point Equivalency", (PetscObject)maxNumRelateObj));
    3662:       PetscCall(PetscContainerDestroy(&maxNumRelateObj));
    3663:     } else {
    3664:       PetscCall(PetscContainerSetPointer(maxNumRelateObj, maxNumRelatePtr));
    3665:     }

    3667:     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point - Face Map", (PetscObject *)&cp_faceObj));
    3668:     if (!cp_faceObj) {
    3669:       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &cp_faceObj));
    3670:       PetscCall(PetscContainerSetPointer(cp_faceObj, cp_face));
    3671:       PetscCall(PetscContainerSetCtxDestroy(cp_faceObj, PetscCtxDestroyDefault));
    3672:       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point - Face Map", (PetscObject)cp_faceObj));
    3673:       PetscCall(PetscContainerDestroy(&cp_faceObj));
    3674:     } else {
    3675:       void *tmp;

    3677:       PetscCall(PetscContainerGetPointer(cp_faceObj, &tmp));
    3678:       PetscCall(PetscFree(tmp));
    3679:       PetscCall(PetscContainerSetPointer(cp_faceObj, cp_face));
    3680:     }

    3682:     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight - Face Map", (PetscObject *)&w_faceObj));
    3683:     if (!w_faceObj) {
    3684:       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &w_faceObj));
    3685:       PetscCall(PetscContainerSetPointer(w_faceObj, w_face));
    3686:       PetscCall(PetscContainerSetCtxDestroy(w_faceObj, PetscCtxDestroyDefault));
    3687:       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weight - Face Map", (PetscObject)w_faceObj));
    3688:       PetscCall(PetscContainerDestroy(&w_faceObj));
    3689:     } else {
    3690:       void *tmp;

    3692:       PetscCall(PetscContainerGetPointer(w_faceObj, &tmp));
    3693:       PetscCall(PetscFree(tmp));
    3694:       PetscCall(PetscContainerSetPointer(w_faceObj, w_face));
    3695:     }

    3697:     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point - Edge Map", (PetscObject *)&cp_edgeObj));
    3698:     if (!cp_edgeObj) {
    3699:       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &cp_edgeObj));
    3700:       PetscCall(PetscContainerSetPointer(cp_edgeObj, cp_edge));
    3701:       PetscCall(PetscContainerSetCtxDestroy(cp_edgeObj, PetscCtxDestroyDefault));
    3702:       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point - Edge Map", (PetscObject)cp_edgeObj));
    3703:       PetscCall(PetscContainerDestroy(&cp_edgeObj));
    3704:     } else {
    3705:       void *tmp;

    3707:       PetscCall(PetscContainerGetPointer(cp_edgeObj, &tmp));
    3708:       PetscCall(PetscFree(tmp));
    3709:       PetscCall(PetscContainerSetPointer(cp_edgeObj, cp_edge));
    3710:     }

    3712:     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight - Edge Map", (PetscObject *)&w_edgeObj));
    3713:     if (!w_edgeObj) {
    3714:       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &w_edgeObj));
    3715:       PetscCall(PetscContainerSetPointer(w_edgeObj, w_edge));
    3716:       PetscCall(PetscContainerSetCtxDestroy(w_edgeObj, PetscCtxDestroyDefault));
    3717:       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weight - Edge Map", (PetscObject)w_edgeObj));
    3718:       PetscCall(PetscContainerDestroy(&w_edgeObj));
    3719:     } else {
    3720:       void *tmp;

    3722:       PetscCall(PetscContainerGetPointer(w_edgeObj, &tmp));
    3723:       PetscCall(PetscFree(tmp));
    3724:       PetscCall(PetscContainerSetPointer(w_edgeObj, w_edge));
    3725:     }

    3727:     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point - Vertex Map", (PetscObject *)&cp_vertexObj));
    3728:     if (!cp_vertexObj) {
    3729:       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &cp_vertexObj));
    3730:       PetscCall(PetscContainerSetPointer(cp_vertexObj, cp_vertex));
    3731:       PetscCall(PetscContainerSetCtxDestroy(cp_vertexObj, PetscCtxDestroyDefault));
    3732:       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point - Vertex Map", (PetscObject)cp_vertexObj));
    3733:       PetscCall(PetscContainerDestroy(&cp_vertexObj));
    3734:     } else {
    3735:       void *tmp;

    3737:       PetscCall(PetscContainerGetPointer(cp_vertexObj, &tmp));
    3738:       PetscCall(PetscFree(tmp));
    3739:       PetscCall(PetscContainerSetPointer(cp_vertexObj, cp_vertex));
    3740:     }

    3742:     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight - Vertex Map", (PetscObject *)&w_vertexObj));
    3743:     if (!w_vertexObj) {
    3744:       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &w_vertexObj));
    3745:       PetscCall(PetscContainerSetPointer(w_vertexObj, w_vertex));
    3746:       PetscCall(PetscContainerSetCtxDestroy(w_vertexObj, PetscCtxDestroyDefault));
    3747:       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weight - Vertex Map", (PetscObject)w_vertexObj));
    3748:       PetscCall(PetscContainerDestroy(&w_vertexObj));
    3749:     } else {
    3750:       void *tmp;

    3752:       PetscCall(PetscContainerGetPointer(w_vertexObj, &tmp));
    3753:       PetscCall(PetscFree(tmp));
    3754:       PetscCall(PetscContainerSetPointer(w_vertexObj, w_vertex));
    3755:     }
    3756:   }

    3758:   // Define Matrix to store  Geometry Gradient information dGeom_i/dCPj_i
    3759:   PetscInt       gcntr   = 0;
    3760:   const PetscInt rowSize = 3 * maxNumCPs * totalNumPoints;
    3761:   const PetscInt colSize = 4 * Nf;

    3763:   // Create Point Surface Gradient Matrix
    3764:   PetscCall(MatCreate(PETSC_COMM_WORLD, &pointSurfGrad));
    3765:   PetscCall(MatSetSizes(pointSurfGrad, PETSC_DECIDE, PETSC_DECIDE, rowSize, colSize));
    3766:   PetscCall(MatSetType(pointSurfGrad, MATAIJ));
    3767:   PetscCall(MatSetUp(pointSurfGrad));

    3769:   // Create Hash Table to store Point's stare row in surfaceGrad[][]
    3770:   PetscCall(PetscHMapICreate(&pointSurfGradRow_Start));

    3772:   // Get Coordinates for the DMPlex point
    3773:   DM           cdm;
    3774:   PetscInt     dE, Nv;
    3775:   Vec          coordinatesLocal;
    3776:   PetscScalar *coords = NULL;

    3778:   PetscCall(DMGetCoordinateDM(dm, &cdm));
    3779:   PetscCall(DMGetCoordinateDim(dm, &dE));
    3780:   PetscCall(DMGetCoordinatesLocal(dm, &coordinatesLocal));

    3782:   // CYCLE THROUGH FACEs
    3783:   PetscScalar maxGrad = 0.;
    3784:   PetscCall(VecGetArrayWrite(gradSACPVec, &gradSACP));
    3785:   PetscCall(VecGetArrayWrite(gradSAWVec, &gradSAW));
    3786:   PetscCall(VecGetArrayWrite(gradVCPVec, &gradVCP));
    3787:   PetscCall(VecGetArrayWrite(gradVWVec, &gradVW));
    3788:   for (int f = 0; f < Nf; ++f) {
    3789:     ego             face = fobjs[f];
    3790:     ego            *eobjs, *nobjs;
    3791:     PetscInt        fid, Ne, Nn;
    3792:     DMLabel         faceLabel, edgeLabel, nodeLabel;
    3793:     PetscHMapI      currFaceUniquePoints = NULL;
    3794:     IS              facePoints, edgePoints, nodePoints;
    3795:     const PetscInt *fIndices, *eIndices, *nIndices;
    3796:     PetscInt        fSize, eSize, nSize;
    3797:     PetscHashIter   fHashKeyIter, eHashKeyIter, nHashKeyIter, pHashKeyIter;
    3798:     PetscBool       fHashKeyFound, eHashKeyFound, nHashKeyFound, pHashKeyFound;
    3799:     PetscInt        cfCntr = 0;

    3801:     // Get Geometry Object for the Current FACE
    3802:     if (islite) {
    3803:       PetscCall(EGlite_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
    3804:       PetscCall(EGlite_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
    3805:     } else {
    3806:       PetscCall(EG_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
    3807:       PetscCall(EG_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
    3808:     }

    3810:     // Get all EDGE and NODE objects attached to the current FACE
    3811:     if (islite) {
    3812:       PetscCall(EGlite_getBodyTopos(body, face, EDGE, &Ne, &eobjs));
    3813:       PetscCall(EGlite_getBodyTopos(body, face, NODE, &Nn, &nobjs));
    3814:     } else {
    3815:       PetscCall(EG_getBodyTopos(body, face, EDGE, &Ne, &eobjs));
    3816:       PetscCall(EG_getBodyTopos(body, face, NODE, &Nn, &nobjs));
    3817:     }

    3819:     // Get all DMPlex Points that have DMLabel "EGADS Face ID" and store them in a Hash Table for later use
    3820:     if (islite) {
    3821:       fid = EGlite_indexBodyTopo(body, face);
    3822:     } else {
    3823:       fid = EG_indexBodyTopo(body, face);
    3824:     }

    3826:     PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
    3827:     PetscCall(DMLabelGetStratumIS(faceLabel, fid, &facePoints));
    3828:     PetscCall(ISGetIndices(facePoints, &fIndices));
    3829:     PetscCall(ISGetSize(facePoints, &fSize));

    3831:     PetscCall(PetscHMapICreate(&currFaceUniquePoints));

    3833:     for (int jj = 0; jj < fSize; ++jj) {
    3834:       PetscCall(PetscHMapIFind(currFaceUniquePoints, fIndices[jj], &fHashKeyIter, &fHashKeyFound));

    3836:       if (!fHashKeyFound) {
    3837:         PetscCall(PetscHMapISet(currFaceUniquePoints, fIndices[jj], cfCntr));
    3838:         cfCntr += 1;
    3839:       }

    3841:       PetscCall(PetscHMapIFind(pointSurfGradRow_Start, fIndices[jj], &pHashKeyIter, &pHashKeyFound));

    3843:       if (!pHashKeyFound) {
    3844:         PetscCall(PetscHMapISet(pointSurfGradRow_Start, fIndices[jj], gcntr));
    3845:         gcntr += 3 * maxNumCPs;
    3846:       }
    3847:     }
    3848:     PetscCall(ISRestoreIndices(facePoints, &fIndices));
    3849:     PetscCall(ISDestroy(&facePoints));

    3851:     // Get all DMPlex Points that have DMLable "EGADS Edge ID" attached to the current FACE and store them in a Hash Table for later use.
    3852:     for (int jj = 0; jj < Ne; ++jj) {
    3853:       ego       edge = eobjs[jj];
    3854:       PetscBool containLabelValue;

    3856:       if (islite) {
    3857:         id = EGlite_indexBodyTopo(body, edge);
    3858:       } else {
    3859:         id = EG_indexBodyTopo(body, edge);
    3860:       }

    3862:       PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
    3863:       PetscCall(DMLabelHasValue(edgeLabel, id, &containLabelValue));

    3865:       if (containLabelValue) {
    3866:         PetscCall(DMLabelGetStratumIS(edgeLabel, id, &edgePoints));
    3867:         PetscCall(ISGetIndices(edgePoints, &eIndices));
    3868:         PetscCall(ISGetSize(edgePoints, &eSize));

    3870:         for (int kk = 0; kk < eSize; ++kk) {
    3871:           PetscCall(PetscHMapIFind(currFaceUniquePoints, eIndices[kk], &eHashKeyIter, &eHashKeyFound));

    3873:           if (!eHashKeyFound) {
    3874:             PetscCall(PetscHMapISet(currFaceUniquePoints, eIndices[kk], cfCntr));
    3875:             cfCntr += 1;
    3876:           }

    3878:           PetscCall(PetscHMapIFind(pointSurfGradRow_Start, eIndices[kk], &pHashKeyIter, &pHashKeyFound));

    3880:           if (!pHashKeyFound) {
    3881:             PetscCall(PetscHMapISet(pointSurfGradRow_Start, eIndices[kk], gcntr));
    3882:             gcntr += 3 * maxNumCPs;
    3883:           }
    3884:         }
    3885:         PetscCall(ISRestoreIndices(edgePoints, &eIndices));
    3886:         PetscCall(ISDestroy(&edgePoints));
    3887:       }
    3888:     }

    3890:     // Get all DMPlex Points that have DMLabel "EGADS Vertex ID" attached to the current FACE and store them in a Hash Table for later use.
    3891:     for (int jj = 0; jj < Nn; ++jj) {
    3892:       ego node = nobjs[jj];

    3894:       if (islite) {
    3895:         id = EGlite_indexBodyTopo(body, node);
    3896:       } else {
    3897:         id = EG_indexBodyTopo(body, node);
    3898:       }

    3900:       PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &nodeLabel));
    3901:       PetscCall(DMLabelGetStratumIS(nodeLabel, id, &nodePoints));
    3902:       PetscCall(ISGetIndices(nodePoints, &nIndices));
    3903:       PetscCall(ISGetSize(nodePoints, &nSize));

    3905:       for (int kk = 0; kk < nSize; ++kk) {
    3906:         PetscCall(PetscHMapIFind(currFaceUniquePoints, nIndices[kk], &nHashKeyIter, &nHashKeyFound));

    3908:         if (!nHashKeyFound) {
    3909:           PetscCall(PetscHMapISet(currFaceUniquePoints, nIndices[kk], cfCntr));
    3910:           cfCntr += 1;
    3911:         }

    3913:         PetscCall(PetscHMapIFind(pointSurfGradRow_Start, nIndices[kk], &pHashKeyIter, &pHashKeyFound));
    3914:         if (!pHashKeyFound) {
    3915:           PetscCall(PetscHMapISet(pointSurfGradRow_Start, nIndices[kk], gcntr));
    3916:           gcntr += 3 * maxNumCPs;
    3917:         }
    3918:       }
    3919:       PetscCall(ISRestoreIndices(nodePoints, &nIndices));
    3920:       PetscCall(ISDestroy(&nodePoints));
    3921:     }

    3923:     // Get the Total Number of entries in the Hash Table
    3924:     PetscInt currFaceUPSize;
    3925:     PetscCall(PetscHMapIGetSize(currFaceUniquePoints, &currFaceUPSize));

    3927:     // Get Keys
    3928:     PetscInt currFaceUPKeys[currFaceUPSize], off = 0;
    3929:     PetscCall(PetscHMapIGetKeys(currFaceUniquePoints, &off, currFaceUPKeys));
    3930:     PetscCall(PetscHMapIDestroy(&currFaceUniquePoints));

    3932:     // Get Current Face Surface Area
    3933:     PetscScalar fSA, faceData[14];
    3934:     PetscCall(EG_getMassProperties(face, faceData)); // This doesn't have a EGlite version. Will it work for EGADSlite files??  KNOWN_ISSUE
    3935:     fSA = faceData[1];

    3937:     // Get Start Row in cpEquiv Matrix
    3938:     PetscHashIter Witer;
    3939:     PetscBool     Wfound;
    3940:     PetscInt      faceWStartRow;
    3941:     PetscCall(PetscHMapIFind(faceCPWeightsRow_Start, fid, &Witer, &Wfound));
    3942:     PetscCheck(Wfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "FACE ID not found in Control Point Weights Hash Table");
    3943:     PetscCall(PetscHMapIGet(faceCPWeightsRow_Start, fid, &faceWStartRow));

    3945:     // Cycle through all points on the current FACE
    3946:     for (int jj = 0; jj < currFaceUPSize; ++jj) {
    3947:       PetscInt currPointID = currFaceUPKeys[jj];
    3948:       PetscCall(DMPlexVecGetClosure(cdm, NULL, coordinatesLocal, currPointID, &Nv, &coords));

    3950:       // Get UV position of FACE
    3951:       double params[2], range[4], eval[18];
    3952:       int    peri;

    3954:       if (islite) PetscCall(EGlite_getRange(face, range, &peri));
    3955:       else PetscCall(EG_getRange(face, range, &peri));

    3957:       PetscCall(DMPlex_Geom_FACE_XYZtoUV_Internal(coords, face, range, 0, dE, params, islite));

    3959:       if (islite) PetscCall(EGlite_evaluate(face, params, eval));
    3960:       else PetscCall(EG_evaluate(face, params, eval));

    3962:       // Make a new SURFACE Geometry by changing the location of the Control Points
    3963:       int    prvSize = bpinfo[3] + bpinfo[6] + (4 * bpinfo[2] * bpinfo[5]);
    3964:       double nbprv[prvSize];

    3966:       // Cycle through each Control Point
    3967:       double denomNew, denomOld;
    3968:       double deltaCoord = 1.0E-4;
    3969:       int    offset     = bpinfo[3] + bpinfo[6];
    3970:       int    wOffset    = offset + (3 * bpinfo[2] * bpinfo[5]);
    3971:       for (int ii = 0; ii < bpinfo[2] * bpinfo[5]; ++ii) {
    3972:         PetscCheck(face->blind, PETSC_COMM_SELF, PETSC_ERR_LIB, "Face %d is corrupted: %d %d", f, jj, ii);
    3973:   #if 0
    3974:         // Cycle through each direction (x, then y, then z)
    3975:         if (jj == 0) {
    3976:           // Get the Number Control Points that are the same as the current points
    3977:           //    We are looking for repeated Control Points
    3978:           PetscInt commonCPcntr = 0;
    3979:           for (int mm = 0; mm < bpinfo[2]*bpinfo[5]; ++mm) {
    3980:             PetscScalar matValue;
    3981:             PetscCall(MatGetValue(cpEquiv, faceWStartRow + ii, faceWStartRow + mm, &matValue));

    3983:             if (matValue > 0.0) commonCPcntr += 1;
    3984:           }
    3985:         }
    3986:   #endif

    3988:         for (int kk = 0; kk < 4; ++kk) {
    3989:           // Reinitialize nbprv[] values because we only want to change one value at a time
    3990:           for (int mm = 0; mm < prvSize; ++mm) nbprv[mm] = bprv[mm];
    3991:           PetscCheck(face->blind, PETSC_COMM_SELF, PETSC_ERR_LIB, "Face %d is corrupted: %d %d %d", f, jj, ii, kk);

    3993:           if (kk == 0) { //X
    3994:             nbprv[offset + 0] = bprv[offset + 0] + deltaCoord;
    3995:             nbprv[offset + 1] = bprv[offset + 1];
    3996:             nbprv[offset + 2] = bprv[offset + 2];
    3997:             denomNew          = nbprv[offset + 0];
    3998:             denomOld          = bprv[offset + 0];
    3999:           } else if (kk == 1) { //Y
    4000:             nbprv[offset + 0] = bprv[offset + 0];
    4001:             nbprv[offset + 1] = bprv[offset + 1] + deltaCoord;
    4002:             nbprv[offset + 2] = bprv[offset + 2];
    4003:             denomNew          = nbprv[offset + 1];
    4004:             denomOld          = bprv[offset + 1];
    4005:           } else if (kk == 2) { //Z
    4006:             nbprv[offset + 0] = bprv[offset + 0];
    4007:             nbprv[offset + 1] = bprv[offset + 1];
    4008:             nbprv[offset + 2] = bprv[offset + 2] + deltaCoord;
    4009:             denomNew          = nbprv[offset + 2];
    4010:             denomOld          = bprv[offset + 2];
    4011:           } else if (kk == 3) { // Weights
    4012:             nbprv[wOffset + ii] = bprv[wOffset + ii] + deltaCoord;
    4013:             denomNew            = nbprv[wOffset + ii];
    4014:             denomOld            = bprv[wOffset + ii];
    4015:           } else {
    4016:             // currently do nothing
    4017:           }

    4019:           // Create New Surface Based on New Control Points or Weights
    4020:           ego newgeom, context;
    4021:           PetscCallEGADS(EG_getContext, (face, &context));                                             // This does not have an EGlite_ version KNOWN_ISSUE
    4022:           PetscCallEGADS(EG_makeGeometry, (context, SURFACE, BSPLINE, NULL, bpinfo, nbprv, &newgeom)); // This does not have an EGlite_ version KNOWN_ISSUE
    4023:           PetscCheck(face->blind, PETSC_COMM_SELF, PETSC_ERR_LIB, "Face %d is corrupted: %d %d %d", f, jj, ii, kk);

    4025:           // Evaluate new (x, y, z) Point Position based on new Surface Definition
    4026:           double newCoords[18];
    4027:           if (islite) PetscCall(EGlite_getRange(newgeom, range, &peri));
    4028:           else PetscCall(EG_getRange(newgeom, range, &peri));

    4030:           PetscCall(DMPlex_Geom_FACE_XYZtoUV_Internal(coords, face, range, 0, dE, params, islite));
    4031:           PetscCheck(face->blind, PETSC_COMM_SELF, PETSC_ERR_LIB, "Face %d is corrupted: %d %d %d", f, jj, ii, kk);

    4033:           if (islite) PetscCall(EGlite_evaluate(newgeom, params, newCoords));
    4034:           else PetscCall(EG_evaluate(newgeom, params, newCoords));

    4036:           // Calculate Surface Area Gradients wrt Control Points and Weights using the local discrete FACE only
    4037:           //      NOTE 1: Will not provide Volume Gradient wrt to Control Points and Weights.
    4038:           //      NOTE 2: This is faster than below where an entire new solid geometry is created for each
    4039:           //              Control Point and Weight gradient
    4040:           if (!fullGeomGrad) {
    4041:             // Create new FACE based on new SURFACE geometry
    4042:             if (jj == 0) { // only for 1st DMPlex Point because we only per CP or Weight
    4043:               double newFaceRange[4];
    4044:               int    newFacePeri;
    4045:               if (islite) PetscCall(EGlite_getRange(newgeom, newFaceRange, &newFacePeri));
    4046:               else PetscCall(EG_getRange(newgeom, newFaceRange, &newFacePeri));

    4048:               ego newface;
    4049:               PetscCallEGADS(EG_makeFace, (newgeom, SFORWARD, newFaceRange, &newface)); // Does not have EGlite version KNOWN_ISSUE
    4050:               PetscCheck(face->blind, PETSC_COMM_SELF, PETSC_ERR_LIB, "Face %d is corrupted: %d %d %d", f, jj, ii, kk);

    4052:               // Get New Face Surface Area
    4053:               PetscScalar newfSA, newFaceData[14];
    4054:               PetscCall(EG_getMassProperties(newface, newFaceData)); // Does not have EGlite version KNOWN_ISSUE
    4055:               newfSA = newFaceData[1];
    4056:               PetscCallEGADS(EG_deleteObject, (newface));
    4057:               PetscCheck(face->blind, PETSC_COMM_SELF, PETSC_ERR_LIB, "Face %d is corrupted: %d %d %d", f, jj, ii, kk);

    4059:               // Update Control Points
    4060:               PetscHashIter CPiter, Witer;
    4061:               PetscBool     CPfound, Wfound;
    4062:               PetscInt      faceCPStartRow, faceWStartRow;

    4064:               PetscScalar dSAdCPi;
    4065:               dSAdCPi = (newfSA - fSA) / (denomNew - denomOld);

    4067:               if (kk < 3) {
    4068:                 PetscCall(PetscHMapIFind(faceCntrlPtRow_Start, fid, &CPiter, &CPfound));
    4069:                 PetscCheck(CPfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "FACE ID not found in Control Point Hash Table");
    4070:                 PetscCall(PetscHMapIGet(faceCntrlPtRow_Start, fid, &faceCPStartRow));

    4072:                 gradSACP[faceCPStartRow + (ii * 3) + kk] = dSAdCPi;

    4074:                 if (PetscAbsReal(dSAdCPi) > maxGrad) maxGrad = PetscAbsReal(dSAdCPi);

    4076:               } else if (kk == 3) {
    4077:                 PetscCall(PetscHMapIFind(faceCPWeightsRow_Start, fid, &Witer, &Wfound));
    4078:                 PetscCheck(Wfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "FACE ID not found in Control Point Hash Table");
    4079:                 PetscCall(PetscHMapIGet(faceCPWeightsRow_Start, fid, &faceWStartRow));

    4081:                 gradSAW[faceWStartRow + ii] = dSAdCPi;

    4083:               } else {
    4084:                 // Do Nothing
    4085:               }
    4086:             }
    4087:           }
    4088:           PetscCallEGADS(EG_deleteObject, (newgeom));

    4090:           // Now Calculate the Surface Gradient for the change in x-component Control Point
    4091:           PetscScalar dxdCx = (newCoords[0] - coords[0]) / deltaCoord;
    4092:           PetscScalar dxdCy = (newCoords[1] - coords[1]) / deltaCoord;
    4093:           PetscScalar dxdCz = (newCoords[2] - coords[2]) / deltaCoord;

    4095:           // Store Gradient Information in surfaceGrad[][] Matrix
    4096:           PetscInt startRow;
    4097:           PetscCall(PetscHMapIGet(pointSurfGradRow_Start, currPointID, &startRow));

    4099:           // Store Results in PETSc Mat
    4100:           PetscCall(MatSetValue(pointSurfGrad, startRow + (ii * 3) + 0, ((fid - 1) * 4) + kk, dxdCx, INSERT_VALUES));
    4101:           PetscCall(MatSetValue(pointSurfGrad, startRow + (ii * 3) + 1, ((fid - 1) * 4) + kk, dxdCy, INSERT_VALUES));
    4102:           PetscCall(MatSetValue(pointSurfGrad, startRow + (ii * 3) + 2, ((fid - 1) * 4) + kk, dxdCz, INSERT_VALUES));

    4104:           //PetscCallEGADS(EG_deleteObject, (newgeom));
    4105:           PetscCheck(face->blind, PETSC_COMM_SELF, PETSC_ERR_LIB, "Face is corrupted");
    4106:         }
    4107:         offset += 3;
    4108:       }
    4109:       PetscCall(DMPlexVecRestoreClosure(cdm, NULL, coordinatesLocal, currPointID, &Nv, &coords));
    4110:     }
    4111:   }

    4113:   // Assemble Point Surface Grad Matrix
    4114:   PetscCall(MatAssemblyBegin(pointSurfGrad, MAT_FINAL_ASSEMBLY));
    4115:   PetscCall(MatAssemblyEnd(pointSurfGrad, MAT_FINAL_ASSEMBLY));

    4117:   if (fullGeomGrad) {
    4118:     // Calculate Surface Area and Volume Control Point and Control Point Weight Gradients
    4119:     //    Note: This is much slower than above due to a new solid geometry being created for
    4120:     //          each change in Control Point and Control Point Weight. However, this method
    4121:     //          will provide the Volume Gradient.

    4123:     // Get Current Face Surface Area
    4124:     PetscScalar bodyVol, bodySA, bodyData[14];
    4125:     PetscCall(EG_getMassProperties(body, bodyData)); // Does not have an EGlite version KNOWN_ISSUE
    4126:     bodyVol = bodyData[0];
    4127:     bodySA  = bodyData[1];

    4129:     // Cycle through Control Points
    4130:     for (int ii = 0; ii < totalNumCPs; ++ii) { // ii should also be the row in cpEquiv for the Control Point
    4131:       // Cycle through X, Y, Z, W changes
    4132:       for (int jj = 0; jj < 4; ++jj) {
    4133:         // Cycle Through Faces
    4134:         double denomNew = 0.0, denomOld = 0.0;
    4135:         double deltaCoord = 1.0E-4;
    4136:         ego    newGeom[Nf];
    4137:         ego    newFaces[Nf];
    4138:         for (int kk = 0; kk < Nf; ++kk) {
    4139:           ego      face;
    4140:           PetscInt currFID = kk + 1;

    4142:           if (islite) {
    4143:             // Get Current FACE
    4144:             PetscCallEGADS(EGlite_objectBodyTopo, (body, FACE, currFID, &face));

    4146:             // Get Geometry Object for the Current FACE
    4147:             PetscCallEGADS(EGlite_getTopology, (face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
    4148:             PetscCallEGADS(EGlite_getGeometry, (fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
    4149:           } else {
    4150:             // Get Current FACE
    4151:             PetscCallEGADS(EG_objectBodyTopo, (body, FACE, currFID, &face));

    4153:             // Get Geometry Object for the Current FACE
    4154:             PetscCallEGADS(EG_getTopology, (face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
    4155:             PetscCallEGADS(EG_getGeometry, (fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
    4156:           }

    4158:           // Make a new SURFACE Geometry by changing the location of the Control Points
    4159:           int    prvSize = bpinfo[3] + bpinfo[6] + (4 * bpinfo[2] * bpinfo[5]);
    4160:           double nbprv[prvSize];

    4162:           // Reinitialize nbprv[] values because we only want to change one value at a time
    4163:           for (int mm = 0; mm < prvSize; ++mm) nbprv[mm] = bprv[mm];

    4165:           // Get Control Point Row and Column Start for cpEquiv
    4166:           PetscHashIter Witer;
    4167:           PetscBool     Wfound;
    4168:           PetscInt      faceWStartRow;
    4169:           PetscCall(PetscHMapIFind(faceCPWeightsRow_Start, currFID, &Witer, &Wfound));
    4170:           PetscCheck(Wfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "FACE ID not found in Control Point Weights Hash Table");
    4171:           PetscCall(PetscHMapIGet(faceCPWeightsRow_Start, currFID, &faceWStartRow));

    4173:           // Modify the Current Control Point on this FACE and All Other FACES
    4174:           // IMPORTANT!!! If you do not move all identical Control Points on other FACES
    4175:           //              you will not generate a solid body. You will generate a set of
    4176:           //              disconnected surfaces that have gap(s) between them.
    4177:           int offset  = bpinfo[3] + bpinfo[6];
    4178:           int wOffset = offset + (3 * bpinfo[2] * bpinfo[5]);
    4179:           for (int mm = 0; mm < bpinfo[2] * bpinfo[5]; ++mm) {
    4180:             PetscScalar matValue;
    4181:             PetscCall(MatGetValue(cpEquiv, ii, faceWStartRow + mm, &matValue));

    4183:             if (matValue > 0.0) {
    4184:               if (jj == 0) { //X
    4185:                 nbprv[offset + (3 * mm) + 0] = bprv[offset + (3 * mm) + 0] + deltaCoord;
    4186:                 nbprv[offset + (3 * mm) + 1] = bprv[offset + (3 * mm) + 1];
    4187:                 nbprv[offset + (3 * mm) + 2] = bprv[offset + (3 * mm) + 2];
    4188:                 denomNew                     = nbprv[offset + (3 * mm) + 0];
    4189:                 denomOld                     = bprv[offset + (3 * mm) + 0];
    4190:               } else if (jj == 1) { //Y
    4191:                 nbprv[offset + (3 * mm) + 0] = bprv[offset + (3 * mm) + 0];
    4192:                 nbprv[offset + (3 * mm) + 1] = bprv[offset + (3 * mm) + 1] + deltaCoord;
    4193:                 nbprv[offset + (3 * mm) + 2] = bprv[offset + (3 * mm) + 2];
    4194:                 denomNew                     = nbprv[offset + (3 * mm) + 1];
    4195:                 denomOld                     = bprv[offset + (3 * mm) + 1];
    4196:               } else if (jj == 2) { //Z
    4197:                 nbprv[offset + (3 * mm) + 0] = bprv[offset + (3 * mm) + 0];
    4198:                 nbprv[offset + (3 * mm) + 1] = bprv[offset + (3 * mm) + 1];
    4199:                 nbprv[offset + (3 * mm) + 2] = bprv[offset + (3 * mm) + 2] + deltaCoord;
    4200:                 denomNew                     = nbprv[offset + (3 * mm) + 2];
    4201:                 denomOld                     = bprv[offset + (3 * mm) + 2];
    4202:               } else if (jj == 3) { // Weights
    4203:                 nbprv[wOffset + mm] = bprv[wOffset + mm] + deltaCoord;
    4204:                 denomNew            = nbprv[wOffset + mm];
    4205:                 denomOld            = bprv[wOffset + mm];
    4206:               } else {
    4207:                 // currently do nothing
    4208:               }
    4209:             }
    4210:           }

    4212:           // Create New Surface Based on New Control Points or Weights
    4213:           ego newgeom, context;
    4214:           PetscCallEGADS(EG_getContext, (face, &context));                                             // Does not have an EGlite_ versions   KNOWN_ISSUE
    4215:           PetscCallEGADS(EG_makeGeometry, (context, SURFACE, BSPLINE, NULL, bpinfo, nbprv, &newgeom)); // Does not have an EGlite_ version KNOWN_ISSUE

    4217:           // Create New FACE based on modified geometry
    4218:           double newFaceRange[4];
    4219:           int    newFacePeri;
    4220:           if (islite) PetscCallEGADS(EGlite_getRange, (newgeom, newFaceRange, &newFacePeri));
    4221:           else PetscCallEGADS(EG_getRange, (newgeom, newFaceRange, &newFacePeri));

    4223:           ego newface;
    4224:           PetscCallEGADS(EG_makeFace, (newgeom, SFORWARD, newFaceRange, &newface)); // Does not have an EGlite_ version KNOWN_ISSUE

    4226:           // store new face for later assembly
    4227:           newGeom[kk]  = newgeom;
    4228:           newFaces[kk] = newface;
    4229:         }

    4231:         // X-WANT TO BUILD THE NEW GEOMETRY, X-GET NEW SA AND PERFORM dSA/dCPi CALCS HERE <---
    4232:         // Sew New Faces together to get a new model
    4233:         ego newmodel;
    4234:         PetscCall(EG_sewFaces(Nf, newFaces, 0.0, 0, &newmodel)); // Does not have an EGlite_ version KNOWN_ISSUE

    4236:         // Get Surface Area and Volume of New/Updated Solid Body
    4237:         PetscScalar newData[14];
    4238:         if (islite) PetscCallEGADS(EGlite_getTopology, (newmodel, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
    4239:         else PetscCallEGADS(EG_getTopology, (newmodel, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));

    4241:         ego nbody = bodies[0];
    4242:         PetscCall(EG_getMassProperties(nbody, newData)); // Does not have an EGlite_ version   KNOWN_ISSUE

    4244:         PetscScalar dSAdCPi, dVdCPi;
    4245:         PetscScalar nbodyVol = newData[0], nbodySA = newData[1];

    4247:         // Calculate Gradients wrt to Control Points and Control Points Weights depending on jj value
    4248:         dSAdCPi = (nbodySA - bodySA) / (denomNew - denomOld);
    4249:         dVdCPi  = (nbodyVol - bodyVol) / (denomNew - denomOld);

    4251:         if (jj < 3) {
    4252:           // Gradienst wrt to Control Points
    4253:           gradSACP[(ii * 3) + jj] = dSAdCPi;
    4254:           gradVCP[(ii * 3) + jj]  = dVdCPi;
    4255:         } else if (jj == 3) {
    4256:           // Gradients wrt to Control Point Weights
    4257:           gradSAW[ii] = dSAdCPi;
    4258:           gradVW[ii]  = dVdCPi;
    4259:         } else {
    4260:           // Do Nothing
    4261:         }
    4262:         PetscCallEGADS(EG_deleteObject, (newmodel));
    4263:         for (int kk = 0; kk < Nf; ++kk) {
    4264:           PetscCallEGADS(EG_deleteObject, (newFaces[kk]));
    4265:           PetscCallEGADS(EG_deleteObject, (newGeom[kk]));
    4266:         }
    4267:       }
    4268:     }
    4269:   }
    4270:   PetscCall(VecRestoreArrayWrite(gradSACPVec, &gradSACP));
    4271:   PetscCall(VecRestoreArrayWrite(gradSAWVec, &gradSAW));
    4272:   PetscCall(VecRestoreArrayWrite(gradVCPVec, &gradVCP));
    4273:   PetscCall(VecRestoreArrayWrite(gradVWVec, &gradVW));
    4274:   PetscCall(MatDestroy(&cpEquiv));

    4276:   // Attach Surface Gradient Hash Table and Matrix to DM
    4277:   {
    4278:     PetscContainer surfGradOrgObj;

    4280:     PetscCall(PetscObjectQuery((PetscObject)dm, "Surface Gradient Hash Table", (PetscObject *)&surfGradOrgObj));
    4281:     if (!surfGradOrgObj) {
    4282:       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &surfGradOrgObj));
    4283:       PetscCall(PetscContainerSetPointer(surfGradOrgObj, pointSurfGradRow_Start));
    4284:       PetscCall(PetscContainerSetCtxDestroy(surfGradOrgObj, DestroyHashMap));
    4285:       PetscCall(PetscObjectCompose((PetscObject)dm, "Surface Gradient Hash Table", (PetscObject)surfGradOrgObj));
    4286:       PetscCall(PetscContainerDestroy(&surfGradOrgObj));
    4287:     } else {
    4288:       PetscCall(PetscContainerSetPointer(surfGradOrgObj, pointSurfGradRow_Start));
    4289:     }

    4291:     PetscCall(PetscObjectCompose((PetscObject)dm, "Surface Gradient Matrix", (PetscObject)pointSurfGrad));
    4292:     PetscCall(MatDestroy(&pointSurfGrad));

    4294:     PetscCall(PetscObjectCompose((PetscObject)dm, "Surface Area Control Point Gradient", (PetscObject)gradSACPVec));
    4295:     PetscCall(VecDestroy(&gradSACPVec));

    4297:     PetscCall(PetscObjectCompose((PetscObject)dm, "Surface Area Weights Gradient", (PetscObject)gradSAWVec));
    4298:     PetscCall(VecDestroy(&gradSAWVec));

    4300:     if (fullGeomGrad) {
    4301:       PetscCall(PetscObjectCompose((PetscObject)dm, "Volume Control Point Gradient", (PetscObject)gradVCPVec));
    4302:       PetscCall(PetscObjectCompose((PetscObject)dm, "Volume Weights Gradient", (PetscObject)gradVWVec));
    4303:     }
    4304:     PetscCall(VecDestroy(&gradVCPVec));
    4305:     PetscCall(VecDestroy(&gradVWVec));
    4306:   }

    4308:   // Could be replaced with DMPlexFreeGeomObject()
    4309:   if (islite) EGlite_free(fobjs);
    4310:   else EG_free(fobjs);
    4311:   PetscFunctionReturn(PETSC_SUCCESS);
    4312: #else
    4313:   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "This method requires EGADS support. Reconfigure using --download-egads");
    4314: #endif
    4315: }

    4317: /*@C
    4318:   DMPlexModifyGeomModel - Generates a new EGADS geometry model based in user provided Control Points and Control Points Weights. Optionally, the function will inflate the DM to the new geometry and save the new geometry to a file.

    4320:   Collective

    4322:   Input Parameters:
    4323: + dm          - The DM object representing the mesh with PetscContainer containing an EGADS geometry model
    4324: . comm        - MPI_Comm object
    4325: . newCP       - C Array of [x, y, z] New/Updated Control Point Coordinates defining the geometry (See DMPlexGeomDataAndGrads() for format)
    4326: . newW        - C Array of New/Updated Control Point Weights associated with the Control Points defining the new geometry (See DMPlexGemGrads() for format)
    4327: . autoInflate - PetscBool Flag denoting if the user would like to inflate the DM points to the new geometry.
    4328: . saveGeom    - PetscBool Flag denoting if the user would iike to save the new geometry to a file.
    4329: - stpName     - Char Array indicating the name of the file to save the new geometry to. Extension must be included and will denote type of file written.
    4330:                       *.stp or *.step = STEP File
    4331:                       *.igs or *.iges = IGES File
    4332:                               *.egads = EGADS File
    4333:                                *.brep = BRep File (OpenCASCADE File)

    4335:   Output Parameter:
    4336: . dm - The updated DM object representing the mesh with PetscContainers containing the updated/modified geometry

    4338:   Level: intermediate

    4340:   Note:
    4341:   Functionality not available for DMPlexes with attached EGADSlite geometry files (.egadslite).

    4343: .seealso: `DMPLEX`, `DMCreate()`, `DMPlexCreateGeom()`, `DMPlexGeomDataAndGrads()`
    4344: @*/
    4345: PetscErrorCode DMPlexModifyGeomModel(DM dm, MPI_Comm comm, PetscScalar newCP[], PetscScalar newW[], PetscBool autoInflate, PetscBool saveGeom, const char *stpName) PeNS
    4346: {
    4347: #if defined(PETSC_HAVE_EGADS)
    4348:   /* EGADS/EGADSlite variables */
    4349:   ego context, model, geom, *bodies, *lobjs, *fobjs;
    4350:   int oclass, mtype, *senses, *lsenses;
    4351:   int Nb, Nf, Nl, id;
    4352:   /* PETSc variables */
    4353:   DMLabel        bodyLabel, faceLabel, edgeLabel, vertexLabel;
    4354:   PetscContainer modelObj, cpHashTableObj, wHashTableObj;
    4355:   PetscHMapI     cpHashTable = NULL, wHashTable = NULL;
    4356:   PetscBool      islite = PETSC_FALSE;
    4357: #endif

    4359: #if defined(PETSC_HAVE_EGADS)
    4360:   PetscFunctionBegin;
    4361:   // Look to see if DM has a Container with either a EGADS or EGADSlite Model
    4362:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
    4363:   if (!modelObj) {
    4364:     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
    4365:     islite = PETSC_TRUE;
    4366:   }
    4367:   PetscCheck(!islite, PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot modify geometries defined by EGADSlite (.egadslite)! Please use another geometry file format STEP, IGES, EGADS or BRep");
    4368:   PetscCheck(modelObj, PETSC_COMM_SELF, PETSC_ERR_SUP, "DM does not have a EGADS Geometry Model attached to it!");

    4370:   // Get attached EGADS model (pointer)
    4371:   PetscCall(PetscContainerGetPointer(modelObj, &model));

    4373:   // Look to see if DM has Container for Geometry Control Point Data
    4374:   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Hash Table", (PetscObject *)&cpHashTableObj));
    4375:   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weights Hash Table", (PetscObject *)&wHashTableObj));

    4377:   PetscCheck(cpHashTableObj && wHashTableObj, PETSC_COMM_SELF, PETSC_ERR_SUP, "DM does not have required Geometry Data attached! Please run DMPlexGeomDataAndGrads() Function first.");

    4379:   // Get attached EGADS model Control Point and Weights Hash Tables and Data Arrays (pointer)
    4380:   PetscCall(PetscContainerGetPointer(cpHashTableObj, &cpHashTable));
    4381:   PetscCall(PetscContainerGetPointer(wHashTableObj, &wHashTable));

    4383:   // Get the number of bodies and body objects in the model
    4384:   if (islite) PetscCallEGADS(EGlite_getTopology, (model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
    4385:   else PetscCallEGADS(EG_getTopology, (model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));

    4387:   // Get all Faces on the body
    4388:   ego body = bodies[0];
    4389:   if (islite) PetscCallEGADS(EGlite_getBodyTopos, (body, NULL, FACE, &Nf, &fobjs));
    4390:   else PetscCallEGADS(EG_getBodyTopos, (body, NULL, FACE, &Nf, &fobjs));

    4392:   ego newGeom[Nf];
    4393:   ego newFaces[Nf];

    4395:   // Update Control Point and Weight definitions for each surface
    4396:   for (int jj = 0; jj < Nf; ++jj) {
    4397:     ego     face = fobjs[jj];
    4398:     ego     bRef, bPrev, bNext;
    4399:     ego     fgeom;
    4400:     int     offset;
    4401:     int     boclass, bmtype, *bpinfo;
    4402:     double *bprv;

    4404:     // Get FACE ID and other Geometry Data
    4405:     if (islite) {
    4406:       id = EGlite_indexBodyTopo(body, face);
    4407:       PetscCallEGADS(EGlite_getTopology, (face, &fgeom, &oclass, &mtype, NULL, &Nl, &lobjs, &lsenses));
    4408:       PetscCallEGADS(EGlite_getGeometry, (fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
    4409:       PetscCallEGADS(EGlite_getInfo, (fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
    4410:     } else {
    4411:       id = EG_indexBodyTopo(body, face);
    4412:       PetscCallEGADS(EG_getTopology, (face, &fgeom, &oclass, &mtype, NULL, &Nl, &lobjs, &lsenses));
    4413:       PetscCallEGADS(EG_getGeometry, (fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
    4414:       PetscCallEGADS(EG_getInfo, (fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
    4415:     }

    4417:     // Update Control Points
    4418:     PetscHashIter CPiter, Witer;
    4419:     PetscBool     CPfound, Wfound;
    4420:     PetscInt      faceCPStartRow, faceWStartRow;

    4422:     PetscCall(PetscHMapIFind(cpHashTable, id, &CPiter, &CPfound));
    4423:     PetscCheck(CPfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "FACE ID not found in Control Point Hash Table");
    4424:     PetscCall(PetscHMapIGet(cpHashTable, id, &faceCPStartRow));

    4426:     PetscCall(PetscHMapIFind(wHashTable, id, &Witer, &Wfound));
    4427:     PetscCheck(Wfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "FACE ID not found in Control Point Weights Hash Table");
    4428:     PetscCall(PetscHMapIGet(wHashTable, id, &faceWStartRow));

    4430:     // UPDATE CONTROL POINTS Locations
    4431:     offset = bpinfo[3] + bpinfo[6];
    4432:     for (int ii = 0; ii < 3 * bpinfo[2] * bpinfo[5]; ++ii) bprv[offset + ii] = newCP[faceCPStartRow + ii];

    4434:     // UPDATE CONTROL POINT WEIGHTS
    4435:     offset = bpinfo[3] + bpinfo[6] + 3 * bpinfo[2] * bpinfo[5];
    4436:     for (int ii = 0; ii < bpinfo[2] * bpinfo[5]; ++ii) bprv[offset + ii] = newW[faceWStartRow + ii];

    4438:     // Get Context from FACE
    4439:     context = NULL;
    4440:     PetscCallEGADS(EG_getContext, (face, &context)); // Does not have an EGlite_ version  KNOWN_ISSUE

    4442:     // Create New Surface
    4443:     ego newgeom;
    4444:     PetscCallEGADS(EG_makeGeometry, (context, SURFACE, BSPLINE, NULL, bpinfo, bprv, &newgeom)); // Does not have an EGlite_ version KNOWN_ISSUE

    4446:     // Create new FACE based on new SURFACE geometry
    4447:     double data[4];
    4448:     int    periodic;
    4449:     if (islite) PetscCallEGADS(EGlite_getRange, (newgeom, data, &periodic));
    4450:     else PetscCallEGADS(EG_getRange, (newgeom, data, &periodic));

    4452:     ego newface;
    4453:     PetscCallEGADS(EG_makeFace, (newgeom, SFORWARD, data, &newface)); // Does not have an EGlite_ version KNOWN_ISSUE
    4454:     newGeom[jj]  = newgeom;
    4455:     newFaces[jj] = newface;
    4456:   }
    4457:   // Could be replaced by DMPlexFreeGeomObject
    4458:   if (islite) EGlite_free(fobjs);
    4459:   else EG_free(fobjs);

    4461:   // Sew New Faces together to get a new model
    4462:   ego newmodel;
    4463:   PetscCall(EG_sewFaces(Nf, newFaces, 0.0, 0, &newmodel)); // Does not have an EGlite_ version   KNOWN_ISSUE
    4464:   for (PetscInt f = 0; f < Nf; ++f) {
    4465:     PetscCallEGADS(EG_deleteObject, (newFaces[f]));
    4466:     PetscCallEGADS(EG_deleteObject, (newGeom[f]));
    4467:   }

    4469:   // Get the total number of NODEs on the original geometry. (This will be the same for the new geometry)
    4470:   int  totalNumNode;
    4471:   ego *nobjTotal;
    4472:   if (islite) {
    4473:     PetscCallEGADS(EGlite_getBodyTopos, (body, NULL, NODE, &totalNumNode, &nobjTotal));
    4474:     EGlite_free(nobjTotal);
    4475:   } else {
    4476:     PetscCallEGADS(EG_getBodyTopos, (body, NULL, NODE, &totalNumNode, &nobjTotal));
    4477:     EG_free(nobjTotal);
    4478:   } // Could be replaced with DMPlexFreeGeomObject

    4480:   // Initialize vector to store equivalent NODE indices between the 2 geometries
    4481:   // FORMAT :: vector index is the Original Geometry's NODE ID, the vector Value is the New Geometry's NODE ID
    4482:   int nodeIDEquiv[totalNumNode + 1];

    4484:   // Now we need to Map the NODE and EDGE IDs from each Model
    4485:   if (islite) PetscCallEGADS(EGlite_getBodyTopos, (body, NULL, FACE, &Nf, &fobjs));
    4486:   else PetscCallEGADS(EG_getBodyTopos, (body, NULL, FACE, &Nf, &fobjs));

    4488:   // New CAD
    4489:   ego *newbodies, newgeomtest, *nfobjs;
    4490:   int  nNf, newNb, newoclass, newmtype, *newsenses;
    4491:   if (islite) PetscCallEGADS(EGlite_getTopology, (newmodel, &newgeomtest, &newoclass, &newmtype, NULL, &newNb, &newbodies, &newsenses));
    4492:   else PetscCallEGADS(EG_getTopology, (newmodel, &newgeomtest, &newoclass, &newmtype, NULL, &newNb, &newbodies, &newsenses));

    4494:   ego newbody = newbodies[0];
    4495:   if (islite) PetscCallEGADS(EGlite_getBodyTopos, (newbody, NULL, FACE, &nNf, &nfobjs));
    4496:   else PetscCallEGADS(EG_getBodyTopos, (newbody, NULL, FACE, &nNf, &nfobjs));

    4498:   PetscCheck(newNb == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "ERROR :: newNb > 1 || newNb = %d", newNb);

    4500:   // Find Equivalent Nodes
    4501:   for (int ii = 0; ii < Nf; ++ii) {
    4502:     double fdata[4];
    4503:     int    peri;

    4505:     // Get Current FACE [u, v] Ranges
    4506:     if (islite) PetscCallEGADS(EGlite_getRange, (fobjs[ii], fdata, &peri));
    4507:     else PetscCallEGADS(EG_getRange, (fobjs[ii], fdata, &peri));

    4509:     // Equate NODE IDs between 2 FACEs by working through (u, v) limits of FACE
    4510:     for (int jj = 0; jj < 2; ++jj) {
    4511:       for (int kk = 2; kk < 4; ++kk) {
    4512:         double params[2] = {fdata[jj], fdata[kk]};
    4513:         double eval[18];
    4514:         if (islite) PetscCallEGADS(EGlite_evaluate, (fobjs[ii], params, eval));
    4515:         else PetscCallEGADS(EG_evaluate, (fobjs[ii], params, eval));

    4517:         // Original Body
    4518:         ego *nobjsOrigFace;
    4519:         int  origNn;
    4520:         if (islite) PetscCallEGADS(EGlite_getBodyTopos, (body, fobjs[ii], NODE, &origNn, &nobjsOrigFace));
    4521:         else PetscCallEGADS(EG_getBodyTopos, (body, fobjs[ii], NODE, &origNn, &nobjsOrigFace));

    4523:         double minVal = 1.0E10;
    4524:         double evalCheck[18];
    4525:         int    equivOrigNodeID = -1;
    4526:         for (int mm = 0; mm < origNn; ++mm) {
    4527:           double delta = 1.0E10;
    4528:           if (islite) PetscCallEGADS(EGlite_evaluate, (nobjsOrigFace[mm], NULL, evalCheck));
    4529:           else PetscCallEGADS(EG_evaluate, (nobjsOrigFace[mm], NULL, evalCheck));

    4531:           delta = PetscSqrtReal(PetscSqr(evalCheck[0] - eval[0]) + PetscSqr(evalCheck[1] - eval[1]) + PetscSqr(evalCheck[2] - eval[2]));

    4533:           if (delta < minVal) {
    4534:             if (islite) equivOrigNodeID = EGlite_indexBodyTopo(body, nobjsOrigFace[mm]);
    4535:             else equivOrigNodeID = EG_indexBodyTopo(body, nobjsOrigFace[mm]);

    4537:             minVal = delta;
    4538:           }
    4539:         }
    4540:         // Could be replaced with DMPlexFreeGeomObject
    4541:         if (islite) EGlite_free(nobjsOrigFace);
    4542:         else EG_free(nobjsOrigFace);

    4544:         // New Body
    4545:         ego *nobjsNewFace;
    4546:         int  newNn;
    4547:         if (islite) PetscCallEGADS(EGlite_getBodyTopos, (newbody, nfobjs[ii], NODE, &newNn, &nobjsNewFace));
    4548:         else PetscCallEGADS(EG_getBodyTopos, (newbody, nfobjs[ii], NODE, &newNn, &nobjsNewFace));

    4550:         minVal             = 1.0E10;
    4551:         int equivNewNodeID = -1;
    4552:         for (int mm = 0; mm < newNn; ++mm) {
    4553:           double delta = 1.0E10;
    4554:           if (islite) PetscCallEGADS(EGlite_evaluate, (nobjsNewFace[mm], NULL, evalCheck));
    4555:           else PetscCallEGADS(EG_evaluate, (nobjsNewFace[mm], NULL, evalCheck));

    4557:           delta = PetscSqrtReal(PetscSqr(evalCheck[0] - eval[0]) + PetscSqr(evalCheck[1] - eval[1]) + PetscSqr(evalCheck[2] - eval[2]));

    4559:           if (delta < minVal) {
    4560:             if (islite) equivNewNodeID = EGlite_indexBodyTopo(newbody, nobjsNewFace[mm]);
    4561:             else equivNewNodeID = EG_indexBodyTopo(newbody, nobjsNewFace[mm]);

    4563:             minVal = delta;
    4564:           }
    4565:         }
    4566:         if (islite) EGlite_free(nobjsNewFace);
    4567:         else EG_free(nobjsNewFace);

    4569:         // Store equivalent NODE IDs
    4570:         nodeIDEquiv[equivOrigNodeID] = equivNewNodeID;
    4571:       }
    4572:     }
    4573:   }

    4575:   // Find Equivalent EDGEs
    4576:   //   Get total number of EDGEs on Original Geometry
    4577:   int  totalNumEdge;
    4578:   ego *eobjsOrig;
    4579:   if (islite) {
    4580:     PetscCallEGADS(EGlite_getBodyTopos, (body, NULL, EDGE, &totalNumEdge, &eobjsOrig));
    4581:     EGlite_free(eobjsOrig);
    4582:   } else {
    4583:     PetscCallEGADS(EG_getBodyTopos, (body, NULL, EDGE, &totalNumEdge, &eobjsOrig));
    4584:     EG_free(eobjsOrig);
    4585:   }

    4587:   //   Get total number of EDGEs on New Geometry
    4588:   int  totalNumEdgeNew;
    4589:   ego *eobjsNew;
    4590:   if (islite) {
    4591:     PetscCallEGADS(EGlite_getBodyTopos, (newbody, NULL, EDGE, &totalNumEdgeNew, &eobjsNew));
    4592:     EGlite_free(eobjsNew);
    4593:   } else {
    4594:     PetscCallEGADS(EG_getBodyTopos, (newbody, NULL, EDGE, &totalNumEdgeNew, &eobjsNew));
    4595:     EG_free(eobjsNew);
    4596:   }

    4598:   // Initialize EDGE ID equivalent vector
    4599:   // FORMAT :: vector index is the Original Geometry's EDGE ID, the vector Value is the New Geometry's EDGE ID
    4600:   int edgeIDEquiv[totalNumEdge + 1];

    4602:   // Find Equivalent EDGEs
    4603:   for (int ii = 0; ii < Nf; ++ii) {
    4604:     // Get Original Geometry EDGE's NODEs
    4605:     int numOrigEdge, numNewEdge;
    4606:     if (islite) {
    4607:       PetscCallEGADS(EGlite_getBodyTopos, (body, fobjs[ii], EDGE, &numOrigEdge, &eobjsOrig));
    4608:       PetscCallEGADS(EGlite_getBodyTopos, (newbody, nfobjs[ii], EDGE, &numNewEdge, &eobjsNew));
    4609:     } else {
    4610:       PetscCallEGADS(EG_getBodyTopos, (body, fobjs[ii], EDGE, &numOrigEdge, &eobjsOrig));
    4611:       PetscCallEGADS(EG_getBodyTopos, (newbody, nfobjs[ii], EDGE, &numNewEdge, &eobjsNew));
    4612:     }

    4614:     // new loop below
    4615:     for (int nn = 0; nn < numOrigEdge; ++nn) {
    4616:       ego origEdge = eobjsOrig[nn];
    4617:       ego geomEdgeOrig, *nobjsOrig;
    4618:       int oclassEdgeOrig, mtypeEdgeOrig;
    4619:       int NnOrig, *nsensesEdgeOrig;

    4621:       if (islite) PetscCallEGADS(EGlite_getTopology, (origEdge, &geomEdgeOrig, &oclassEdgeOrig, &mtypeEdgeOrig, NULL, &NnOrig, &nobjsOrig, &nsensesEdgeOrig));
    4622:       else PetscCallEGADS(EG_getTopology, (origEdge, &geomEdgeOrig, &oclassEdgeOrig, &mtypeEdgeOrig, NULL, &NnOrig, &nobjsOrig, &nsensesEdgeOrig));

    4624:       PetscBool isSame = PETSC_FALSE;
    4625:       for (int jj = 0; jj < numNewEdge; ++jj) {
    4626:         ego newEdge = eobjsNew[jj];
    4627:         ego geomEdgeNew, *nobjsNew;
    4628:         int oclassEdgeNew, mtypeEdgeNew;
    4629:         int NnNew, *nsensesEdgeNew;

    4631:         if (islite) PetscCallEGADS(EGlite_getTopology, (newEdge, &geomEdgeNew, &oclassEdgeNew, &mtypeEdgeNew, NULL, &NnNew, &nobjsNew, &nsensesEdgeNew));
    4632:         else PetscCallEGADS(EG_getTopology, (newEdge, &geomEdgeNew, &oclassEdgeNew, &mtypeEdgeNew, NULL, &NnNew, &nobjsNew, &nsensesEdgeNew));

    4634:         if (mtypeEdgeOrig == mtypeEdgeNew) {
    4635:           // Only operate if the EDGE types are the same
    4636:           for (int kk = 0; kk < NnNew; ++kk) {
    4637:             int nodeIDOrigGeom, nodeIDNewGeom;
    4638:             if (islite) {
    4639:               nodeIDOrigGeom = EGlite_indexBodyTopo(body, nobjsOrig[kk]);
    4640:               nodeIDNewGeom  = EGlite_indexBodyTopo(newbody, nobjsNew[kk]);
    4641:             } else {
    4642:               nodeIDOrigGeom = EG_indexBodyTopo(body, nobjsOrig[kk]);
    4643:               nodeIDNewGeom  = EG_indexBodyTopo(newbody, nobjsNew[kk]);
    4644:             }

    4646:             if (nodeIDNewGeom == nodeIDEquiv[nodeIDOrigGeom]) {
    4647:               isSame = PETSC_TRUE;
    4648:             } else {
    4649:               isSame = PETSC_FALSE;
    4650:               kk     = NnNew; // skip ahead because first NODE failed test and order is important
    4651:             }
    4652:           }

    4654:           if (isSame == PETSC_TRUE) {
    4655:             int edgeIDOrig, edgeIDNew;
    4656:             if (islite) {
    4657:               edgeIDOrig = EGlite_indexBodyTopo(body, origEdge);
    4658:               edgeIDNew  = EGlite_indexBodyTopo(newbody, newEdge);
    4659:             } else {
    4660:               edgeIDOrig = EG_indexBodyTopo(body, origEdge);
    4661:               edgeIDNew  = EG_indexBodyTopo(newbody, newEdge);
    4662:             }

    4664:             edgeIDEquiv[edgeIDOrig] = edgeIDNew;
    4665:             jj                      = numNewEdge;
    4666:           }
    4667:         }
    4668:       }
    4669:     }
    4670:     if (islite) {
    4671:       EGlite_free(eobjsOrig);
    4672:       EGlite_free(eobjsNew);
    4673:     } else {
    4674:       EG_free(eobjsOrig);
    4675:       EG_free(eobjsNew);
    4676:     }
    4677:   }
    4678:   if (islite) {
    4679:     EGlite_free(fobjs);
    4680:     EGlite_free(nfobjs);
    4681:   } else {
    4682:     EG_free(fobjs);
    4683:     EG_free(nfobjs);
    4684:   }

    4686:   // Modify labels to point to the IDs on the new Geometry
    4687:   IS isNodeID, isEdgeID;

    4689:   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
    4690:   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
    4691:   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
    4692:   PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));

    4694:   PetscCall(ISCreateGeneral(comm, totalNumNode + 1, nodeIDEquiv, PETSC_COPY_VALUES, &isNodeID));
    4695:   PetscCall(ISCreateGeneral(comm, totalNumEdge + 1, edgeIDEquiv, PETSC_COPY_VALUES, &isEdgeID));
    4696:   /* Do not perform check. Np may != Nv due to Degenerate Geometry which is not stored in labels.               */
    4697:   /* We do not know in advance which IDs have been omitted. This may also change due to geometry modifications. */
    4698:   PetscCall(DMLabelRewriteValues(vertexLabel, isNodeID));
    4699:   PetscCall(DMLabelRewriteValues(edgeLabel, isEdgeID));
    4700:   PetscCall(ISDestroy(&isNodeID));
    4701:   PetscCall(ISDestroy(&isEdgeID));

    4703:   // Attempt to point to the new geometry
    4704:   PetscCallEGADS(EG_deleteObject, (model));
    4705:   PetscCall(PetscContainerSetPointer(modelObj, newmodel));

    4707:   // save updated model to file
    4708:   if (saveGeom == PETSC_TRUE && stpName != NULL) PetscCall(EG_saveModel(newmodel, stpName));

    4710:   // Inflate Mesh to EGADS Model
    4711:   if (autoInflate == PETSC_TRUE) PetscCall(DMPlexInflateToGeomModel(dm, PETSC_TRUE));
    4712:   PetscFunctionReturn(PETSC_SUCCESS);
    4713: #else
    4714:   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "This method requires EGADS support. Reconfigure using --download-egads");
    4715: #endif
    4716: }

    4718: /*@C
    4719:   DMPlexGetGeomModelTUV - Gets the [t] (EDGES) and [u, v] (FACES) geometry parameters of DM points that are associated geometry relationships. Requires a DM with a EGADS model attached.

    4721:   Collective

    4723:   Input Parameter:
    4724: . dm - The DM object representing the mesh with PetscContainer containing an EGADS geometry model

    4726:   Level: intermediate

    4728: .seealso: `DMPLEX`, `DMCreate()`, `DMPlexCreateGeom()`, `DMPlexGeomDataAndGrads()`
    4729: @*/
    4730: PetscErrorCode DMPlexGetGeomModelTUV(DM dm) PeNS
    4731: {
    4732: #if defined(PETSC_HAVE_EGADS)
    4733:   /* EGADS Variables */
    4734:   ego    model, geom, body, face, edge;
    4735:   ego   *bodies;
    4736:   int    Nb, oclass, mtype, *senses;
    4737:   double result[4];
    4738:   /* PETSc Variables */
    4739:   DM             cdm;
    4740:   PetscContainer modelObj;
    4741:   DMLabel        bodyLabel, faceLabel, edgeLabel, vertexLabel;
    4742:   Vec            coordinates;
    4743:   PetscScalar   *coords;
    4744:   PetscInt       bodyID, faceID, edgeID, vertexID;
    4745:   PetscInt       cdim, vStart, vEnd, v;
    4746:   PetscBool      islite = PETSC_FALSE;
    4747: #endif

    4749:   PetscFunctionBegin;
    4750: #if defined(PETSC_HAVE_EGADS)
    4751:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
    4752:   if (!modelObj) {
    4753:     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
    4754:     islite = PETSC_TRUE;
    4755:   }
    4756:   if (!modelObj) PetscFunctionReturn(PETSC_SUCCESS);

    4758:   PetscCall(DMGetCoordinateDim(dm, &cdim));
    4759:   PetscCall(DMGetCoordinateDM(dm, &cdm));
    4760:   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
    4761:   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
    4762:   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
    4763:   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
    4764:   PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));

    4766:   PetscCall(PetscContainerGetPointer(modelObj, &model));

    4768:   if (islite) PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
    4769:   else PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));

    4771:   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
    4772:   PetscCall(VecGetArrayWrite(coordinates, &coords));

    4774:   // Define t, u, v arrays to be stored in a PetscContainer after populated
    4775:   PetscScalar *t_point, *u_point, *v_point;
    4776:   PetscCall(PetscMalloc1(vEnd - vStart, &t_point));
    4777:   PetscCall(PetscMalloc1(vEnd - vStart, &u_point));
    4778:   PetscCall(PetscMalloc1(vEnd - vStart, &v_point));

    4780:   for (v = vStart; v < vEnd; ++v) {
    4781:     PetscScalar *vcoords;

    4783:     PetscCall(DMLabelGetValue(bodyLabel, v, &bodyID));
    4784:     PetscCall(DMLabelGetValue(faceLabel, v, &faceID));
    4785:     PetscCall(DMLabelGetValue(edgeLabel, v, &edgeID));
    4786:     PetscCall(DMLabelGetValue(vertexLabel, v, &vertexID));

    4788:     // TODO Figure out why this is unknown sometimes
    4789:     if (bodyID < 0 && Nb == 1) bodyID = 0;
    4790:     PetscCheck(bodyID >= 0 && bodyID < Nb, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Body %" PetscInt_FMT " for vertex %" PetscInt_FMT " is not in [0, %d)", bodyID, v, Nb);
    4791:     body = bodies[bodyID];

    4793:     PetscCall(DMPlexPointLocalRef(cdm, v, coords, (void *)&vcoords));
    4794:     if (edgeID > 0) {
    4795:       /* Snap to EDGE at nearest location */
    4796:       double params[1];

    4798:       if (islite) {
    4799:         PetscCall(EGlite_objectBodyTopo(body, EDGE, edgeID, &edge));
    4800:         PetscCall(EGlite_invEvaluate(edge, vcoords, params, result));
    4801:       } // Get (t) of nearest point on EDGE
    4802:       else {
    4803:         PetscCall(EG_objectBodyTopo(body, EDGE, edgeID, &edge));
    4804:         PetscCall(EG_invEvaluate(edge, vcoords, params, result));
    4805:       } // Get (t) of nearest point on EDGE

    4807:       t_point[v - vStart] = params[0];
    4808:       u_point[v - vStart] = 0.0;
    4809:       v_point[v - vStart] = 0.0;
    4810:     } else if (faceID > 0) {
    4811:       /* Snap to FACE at nearest location */
    4812:       double params[2];

    4814:       if (islite) {
    4815:         PetscCall(EGlite_objectBodyTopo(body, FACE, faceID, &face));
    4816:         PetscCall(EGlite_invEvaluate(face, vcoords, params, result));
    4817:       } // Get (x,y,z) of nearest point on FACE
    4818:       else {
    4819:         PetscCall(EG_objectBodyTopo(body, FACE, faceID, &face));
    4820:         PetscCall(EG_invEvaluate(face, vcoords, params, result));
    4821:       } // Get (x,y,z) of nearest point on FACE

    4823:       t_point[v - vStart] = 0.0;
    4824:       u_point[v - vStart] = params[0];
    4825:       v_point[v - vStart] = params[1];
    4826:     } else {
    4827:       t_point[v - vStart] = 0.0;
    4828:       u_point[v - vStart] = 0.0;
    4829:       v_point[v - vStart] = 0.0;
    4830:     }
    4831:   }
    4832:   PetscCall(VecRestoreArrayWrite(coordinates, &coords));
    4833:   /* Clear out global coordinates */
    4834:   PetscCall(VecDestroy(&dm->coordinates[0].x));

    4836:   /* Store in PetscContainters */
    4837:   {
    4838:     PetscContainer t_pointObj, u_pointObj, v_pointObj;

    4840:     PetscCall(PetscObjectQuery((PetscObject)dm, "Point - Edge t Parameter", (PetscObject *)&t_pointObj));
    4841:     if (!t_pointObj) {
    4842:       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &t_pointObj));
    4843:       PetscCall(PetscContainerSetPointer(t_pointObj, t_point));
    4844:       PetscCall(PetscObjectCompose((PetscObject)dm, "Point - Edge t Parameter", (PetscObject)t_pointObj));
    4845:       PetscCall(PetscContainerSetCtxDestroy(t_pointObj, PetscCtxDestroyDefault));
    4846:       PetscCall(PetscContainerDestroy(&t_pointObj));
    4847:     } else {
    4848:       void *old;

    4850:       PetscCall(PetscContainerGetPointer(t_pointObj, &old));
    4851:       PetscCall(PetscFree(old));
    4852:       PetscCall(PetscContainerSetPointer(t_pointObj, t_point));
    4853:     }

    4855:     PetscCall(PetscObjectQuery((PetscObject)dm, "Point - Face u Parameter", (PetscObject *)&u_pointObj));
    4856:     if (!u_pointObj) {
    4857:       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &u_pointObj));
    4858:       PetscCall(PetscContainerSetPointer(u_pointObj, u_point));
    4859:       PetscCall(PetscObjectCompose((PetscObject)dm, "Point - Face u Parameter", (PetscObject)u_pointObj));
    4860:       PetscCall(PetscContainerSetCtxDestroy(u_pointObj, PetscCtxDestroyDefault));
    4861:       PetscCall(PetscContainerDestroy(&u_pointObj));
    4862:     } else {
    4863:       void *old;

    4865:       PetscCall(PetscContainerGetPointer(u_pointObj, &old));
    4866:       PetscCall(PetscFree(old));
    4867:       PetscCall(PetscContainerSetPointer(u_pointObj, u_point));
    4868:     }

    4870:     PetscCall(PetscObjectQuery((PetscObject)dm, "Point - Face v Parameter", (PetscObject *)&v_pointObj));
    4871:     if (!v_pointObj) {
    4872:       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &v_pointObj));
    4873:       PetscCall(PetscContainerSetPointer(v_pointObj, v_point));
    4874:       PetscCall(PetscObjectCompose((PetscObject)dm, "Point - Face v Parameter", (PetscObject)v_pointObj));
    4875:       PetscCall(PetscContainerSetCtxDestroy(v_pointObj, PetscCtxDestroyDefault));
    4876:       PetscCall(PetscContainerDestroy(&v_pointObj));
    4877:     } else {
    4878:       void *old;

    4880:       PetscCall(PetscContainerGetPointer(v_pointObj, &old));
    4881:       PetscCall(PetscFree(old));
    4882:       PetscCall(PetscContainerSetPointer(v_pointObj, v_point));
    4883:     }
    4884:   }
    4885: #endif
    4886:   PetscFunctionReturn(PETSC_SUCCESS);
    4887: }

    4889: /*@C
    4890:   DMPlexInflateToGeomModelUseTUV - Inflates the DM to the associated underlying geometry using the [t] {EDGES) and [u, v] (FACES} associated parameters. Requires a DM with an EGADS model attached and a previous call to DMPlexGetGeomModelTUV().

    4892:   Collective

    4894:   Input Parameter:
    4895: . dm - The DM object representing the mesh with PetscContainer containing an EGADS geometry model

    4897:   Level: intermediate

    4899:   Note:
    4900:   The updated DM object inflated to the associated underlying geometry. This updates the [x, y, z] coordinates of DM points associated with geometry.

    4902: .seealso: `DMPLEX`, `DMCreate()`, `DMPlexCreateGeom()`, `DMPlexGeomDataAndGrads()`, `DMPlexGetGeomModelTUV()`
    4903: @*/
    4904: PetscErrorCode DMPlexInflateToGeomModelUseTUV(DM dm) PeNS
    4905: {
    4906: #if defined(PETSC_HAVE_EGADS)
    4907:   /* EGADS Variables */
    4908:   ego    model, geom, body, face, edge, vertex;
    4909:   ego   *bodies;
    4910:   int    Nb, oclass, mtype, *senses;
    4911:   double result[18], params[2];
    4912:   /* PETSc Variables */
    4913:   DM             cdm;
    4914:   PetscContainer modelObj;
    4915:   PetscContainer t_pointObj, u_pointObj, v_pointObj;
    4916:   DMLabel        bodyLabel, faceLabel, edgeLabel, vertexLabel;
    4917:   Vec            coordinates;
    4918:   PetscScalar   *coords;
    4919:   PetscScalar   *t_point, *u_point, *v_point;
    4920:   PetscInt       bodyID, faceID, edgeID, vertexID;
    4921:   PetscInt       cdim, d, vStart, vEnd, v;
    4922:   PetscBool      islite = PETSC_FALSE;
    4923: #endif

    4925:   PetscFunctionBegin;
    4926: #if defined(PETSC_HAVE_EGADS)
    4927:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
    4928:   if (!modelObj) {
    4929:     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
    4930:     islite = PETSC_TRUE;
    4931:   }

    4933:   PetscCall(PetscObjectQuery((PetscObject)dm, "Point - Edge t Parameter", (PetscObject *)&t_pointObj));
    4934:   PetscCall(PetscObjectQuery((PetscObject)dm, "Point - Face u Parameter", (PetscObject *)&u_pointObj));
    4935:   PetscCall(PetscObjectQuery((PetscObject)dm, "Point - Face v Parameter", (PetscObject *)&v_pointObj));

    4937:   if (!modelObj) PetscFunctionReturn(PETSC_SUCCESS);
    4938:   if (!t_pointObj) PetscFunctionReturn(PETSC_SUCCESS);
    4939:   if (!u_pointObj) PetscFunctionReturn(PETSC_SUCCESS);
    4940:   if (!v_pointObj) PetscFunctionReturn(PETSC_SUCCESS);

    4942:   PetscCall(DMGetCoordinateDim(dm, &cdim));
    4943:   PetscCall(DMGetCoordinateDM(dm, &cdm));
    4944:   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
    4945:   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
    4946:   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
    4947:   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
    4948:   PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));

    4950:   PetscCall(PetscContainerGetPointer(t_pointObj, &t_point));
    4951:   PetscCall(PetscContainerGetPointer(u_pointObj, &u_point));
    4952:   PetscCall(PetscContainerGetPointer(v_pointObj, &v_point));

    4954:   PetscCall(PetscContainerGetPointer(modelObj, &model));

    4956:   if (islite) PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
    4957:   else PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));

    4959:   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
    4960:   PetscCall(VecGetArrayWrite(coordinates, &coords));

    4962:   for (v = vStart; v < vEnd; ++v) {
    4963:     PetscScalar *vcoords;

    4965:     PetscCall(DMLabelGetValue(bodyLabel, v, &bodyID));
    4966:     PetscCall(DMLabelGetValue(faceLabel, v, &faceID));
    4967:     PetscCall(DMLabelGetValue(edgeLabel, v, &edgeID));
    4968:     PetscCall(DMLabelGetValue(vertexLabel, v, &vertexID));

    4970:     // TODO Figure out why this is unknown sometimes
    4971:     if (bodyID < 0 && Nb == 1) bodyID = 0;
    4972:     PetscCheck(bodyID >= 0 && bodyID < Nb, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Body %" PetscInt_FMT " for vertex %" PetscInt_FMT " is not in [0, %d)", bodyID, v, Nb);
    4973:     body = bodies[bodyID];

    4975:     PetscCall(DMPlexPointLocalRef(cdm, v, coords, (void *)&vcoords));
    4976:     if (vertexID > 0) {
    4977:       /* Snap to Vertices */
    4978:       if (islite) {
    4979:         PetscCall(EGlite_objectBodyTopo(body, NODE, vertexID, &vertex));
    4980:         PetscCall(EGlite_evaluate(vertex, NULL, result));
    4981:       } else {
    4982:         PetscCall(EG_objectBodyTopo(body, NODE, vertexID, &vertex));
    4983:         PetscCall(EG_evaluate(vertex, NULL, result));
    4984:       }
    4985:       for (d = 0; d < cdim; ++d) vcoords[d] = result[d];
    4986:     } else if (edgeID > 0) {
    4987:       /* Snap to EDGE */
    4988:       params[0] = t_point[v - vStart];
    4989:       if (islite) {
    4990:         PetscCall(EGlite_objectBodyTopo(body, EDGE, edgeID, &edge));
    4991:         PetscCall(EGlite_evaluate(edge, params, result));
    4992:       } else {
    4993:         PetscCall(EG_objectBodyTopo(body, EDGE, edgeID, &edge));
    4994:         PetscCall(EG_evaluate(edge, params, result));
    4995:       }
    4996:       for (d = 0; d < cdim; ++d) vcoords[d] = result[d];
    4997:     } else if (faceID > 0) {
    4998:       /* Snap to FACE */
    4999:       params[0] = u_point[v - vStart];
    5000:       params[1] = v_point[v - vStart];
    5001:       if (islite) {
    5002:         PetscCall(EGlite_objectBodyTopo(body, FACE, faceID, &face));
    5003:         PetscCall(EGlite_evaluate(face, params, result));
    5004:       } else {
    5005:         PetscCall(EG_objectBodyTopo(body, FACE, faceID, &face));
    5006:         PetscCall(EG_evaluate(face, params, result));
    5007:       }
    5008:       for (d = 0; d < cdim; ++d) vcoords[d] = result[d];
    5009:     }
    5010:   }
    5011:   PetscCall(VecRestoreArrayWrite(coordinates, &coords));
    5012:   /* Clear out global coordinates */
    5013:   PetscCall(VecDestroy(&dm->coordinates[0].x));
    5014: #endif
    5015:   PetscFunctionReturn(PETSC_SUCCESS);
    5016: }

    5018: /*@
    5019:   DMPlexInflateToGeomModel - Wrapper function allowing two methods for inflating refined meshes to the underlying geometric domain.

    5021:   Collective

    5023:   Input Parameters:
    5024: + dm     - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
    5025: - useTUV - PetscBool indicating if the user would like to inflate the DMPlex to the underlying geometry
    5026:            using (t) for nodes on EDGEs and (u, v) for nodes on FACEs or using the nodes (x, y, z) coordinates
    5027:            and shortest distance routine.
    5028:             If useTUV = PETSC_TRUE, use the (t) or (u, v) parameters to inflate the DMPlex to the CAD geometry.
    5029:             If useTUV = PETSC_FALSE, use the nodes (x, y, z) coordinates and the shortest disctance routine.

    5031:   Notes:
    5032:   DM with nodal coordinates modified so that they lie on the EDGEs and FACEs of the underlying geometry.

    5034:   (t) and (u, v) parameters for all DMPlex nodes on EDGEs and FACEs are stored in arrays within PetscContainers attached to the DM.
    5035:   The containers have names "Point - Edge t Parameter", "Point - Face u Parameter", and "Point - Face v Parameter".
    5036:   The arrays are organized by Point 0-based ID (i.e. [v-vstart] as defined in the DMPlex.

    5038:   Level: intermediate

    5040: .seealso: `DMPlexGetGeomModelTUV()`, `DMPlexInflateToGeomModelUseTUV()`, `DMPlexInflateToGeomModelUseXYZ()`
    5041: @*/
    5042: PetscErrorCode DMPlexInflateToGeomModel(DM dm, PetscBool useTUV) PeNS
    5043: {
    5044:   PetscFunctionBeginHot;
    5045:   if (useTUV) {
    5046:     PetscCall(DMPlexGetGeomModelTUV(dm));
    5047:     PetscCall(DMPlexInflateToGeomModelUseTUV(dm));
    5048:   } else {
    5049:     PetscCall(DMPlexInflateToGeomModelUseXYZ(dm));
    5050:   }
    5051:   PetscFunctionReturn(PETSC_SUCCESS);
    5052: }

    5054: #ifdef PETSC_HAVE_EGADS
    5055: /*@C
    5056:   DMPlexGetGeomModelBodies - Returns an array of `PetscGeom` BODY objects attached to the referenced geometric model entity as well as the number of BODYs.

    5058:   Collective

    5060:   Input Parameter:
    5061: . dm - The DMPlex object with an attached PetscContainer storing a CAD Geometry object

    5063:   Output Parameters:
    5064: + bodies    - Array of PetscGeom BODY objects referenced by the geometric model.
    5065: - numBodies - Number of BODYs referenced by the geometric model. Also the size of **bodies array.

    5067:   Level: intermediate

    5069: .seealso: `DMPlexGetGeomModelBodyShells()`, `DMPlexGetGeomModelTUV()`, `DMPlexInflateToGeomModelUseTUV()`, `DMPlexInflateToGeomModelUseXYZ()`
    5070: @*/
    5071: PetscErrorCode DMPlexGetGeomModelBodies(DM dm, PetscGeom **bodies, PetscInt *numBodies) PeNS
    5072: {
    5073:   PetscFunctionBeginHot;
    5074:   PetscContainer modelObj;
    5075:   PetscBool      islite = PETSC_FALSE;
    5076:   ego            model, geom;
    5077:   int            oclass, mtype;
    5078:   int           *senses;

    5080:   /* Determine which type of EGADS model is attached to the DM */
    5081:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
    5082:   if (!modelObj) {
    5083:     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
    5084:     islite = PETSC_TRUE;
    5085:   }

    5087:   // Get attached EGADS or EGADSlite model (pointer)
    5088:   PetscCall(PetscContainerGetPointer(modelObj, &model));

    5090:   if (islite) PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, numBodies, bodies, &senses));
    5091:   else PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, numBodies, bodies, &senses));
    5092:   PetscFunctionReturn(PETSC_SUCCESS);
    5093: }

    5095: /*@C
    5096:   DMPlexGetGeomModelBodyShells - Returns an array of `PetscGeom` SHELL objects attached to the referenced BODY geometric entity as well as the number of SHELLs.

    5098:   Collective

    5100:   Input Parameters:
    5101: + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
    5102: - body - PetscGeom BODY object containing the SHELL objects of interest.

    5104:   Output Parameters:
    5105: + shells    - Array of PetscGeom SHELL objects referenced by the PetscGeom BODY object
    5106: - numShells - Number of SHELLs referenced by the PetscGeom BODY object. Also the size of **shells array.

    5108:   Level: intermediate

    5110: .seealso: `DMPlexGetGeomModelBodies()`, `DMPlexGetGeomModelTUV()`, `DMPlexInflateToGeomModelUseTUV()`, `DMPlexInflateToGeomModelUseXYZ()`
    5111: @*/
    5112: PetscErrorCode DMPlexGetGeomModelBodyShells(DM dm, PetscGeom body, PetscGeom **shells, PetscInt *numShells) PeNS
    5113: {
    5114:   PetscFunctionBeginHot;
    5115:   #ifdef PETSC_HAVE_EGADS
    5116:   PetscContainer modelObj;
    5117:   PetscBool      islite = PETSC_FALSE;

    5119:   /* Determine which type of EGADS model is attached to the DM */
    5120:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
    5121:   if (!modelObj) {
    5122:     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
    5123:     islite = PETSC_TRUE;
    5124:   }

    5126:   if (islite) PetscCall(EGlite_getBodyTopos(body, NULL, SHELL, numShells, shells));
    5127:   else PetscCall(EG_getBodyTopos(body, NULL, SHELL, numShells, shells));
    5128:   #endif
    5129:   PetscFunctionReturn(PETSC_SUCCESS);
    5130: }

    5132: /*@C
    5133:   DMPlexGetGeomModelBodyFaces - Returns an array of `PetscGeom` FACE objects attached to the referenced BODY geometric entity as well as the number of FACEs.

    5135:   Collective

    5137:   Input Parameters:
    5138: + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
    5139: - body - PetscGeom BODY object containing the FACE objects of interest.

    5141:   Output Parameters:
    5142: + faces    - Array of PetscGeom FACE objects referenced by the PetscGeom BODY object
    5143: - numFaces - Number of FACEs referenced by the PetscGeom BODY object. Also the size of **faces array.

    5145:   Level: intermediate

    5147: .seealso: `DMPlexGetGeomModelBodies()`, `DMPlexGetGeomModelTUV()`, `DMPlexInflateToGeomModelUseTUV()`, `DMPlexInflateToGeomModelUseXYZ()`
    5148: @*/
    5149: PetscErrorCode DMPlexGetGeomModelBodyFaces(DM dm, PetscGeom body, PetscGeom **faces, PetscInt *numFaces) PeNS
    5150: {
    5151:   PetscFunctionBeginHot;
    5152:   #ifdef PETSC_HAVE_EGADS
    5153:   PetscContainer modelObj;
    5154:   PetscBool      islite = PETSC_FALSE;

    5156:   /* Determine which type of EGADS model is attached to the DM */
    5157:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
    5158:   if (!modelObj) {
    5159:     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
    5160:     islite = PETSC_TRUE;
    5161:   }

    5163:   if (islite) PetscCall(EGlite_getBodyTopos(body, NULL, FACE, numFaces, faces));
    5164:   else PetscCall(EG_getBodyTopos(body, NULL, FACE, numFaces, faces));
    5165:   #endif
    5166:   PetscFunctionReturn(PETSC_SUCCESS);
    5167: }

    5169: /*@C
    5170:   DMPlexGetGeomModelBodyLoops - Returns an array of `PetscGeom` Loop objects attached to the referenced BODY geometric entity as well as the number of LOOPs.

    5172:   Collective

    5174:   Input Parameters:
    5175: + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
    5176: - body - PetscGeom BODY object containing the LOOP objects of interest.

    5178:   Output Parameters:
    5179: + loops    - Array of PetscGeom FACE objects referenced by the PetscGeom SHELL object
    5180: - numLoops - Number of LOOPs referenced by the PetscGeom BODY object. Also the size of **loops array.

    5182:   Level: intermediate

    5184: .seealso: `DMPlexGetGeomModelBodies()`, `DMPlexGetGeomModelTUV()`, `DMPlexInflateToGeomModelUseTUV()`, `DMPlexInflateToGeomModelUseXYZ()`
    5185: @*/
    5186: PetscErrorCode DMPlexGetGeomModelBodyLoops(DM dm, PetscGeom body, PetscGeom **loops, PetscInt *numLoops) PeNS
    5187: {
    5188:   PetscFunctionBeginHot;
    5189:   #ifdef PETSC_HAVE_EGADS
    5190:   PetscContainer modelObj;
    5191:   PetscBool      islite = PETSC_FALSE;

    5193:   /* Determine which type of EGADS model is attached to the DM */
    5194:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
    5195:   if (!modelObj) {
    5196:     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
    5197:     islite = PETSC_TRUE;
    5198:   }

    5200:   if (islite) PetscCall(EGlite_getBodyTopos(body, NULL, LOOP, numLoops, loops));
    5201:   else PetscCall(EG_getBodyTopos(body, NULL, LOOP, numLoops, loops));
    5202:   #endif
    5203:   PetscFunctionReturn(PETSC_SUCCESS);
    5204: }

    5206: /*@C
    5207:   DMPlexGetGeomModelShellFaces - Returns an array of `PetscGeom` FACE objects attached to the referenced SHELL geometric entity as well as the number of FACEs.

    5209:   Collective

    5211:   Input Parameters:
    5212: + dm    - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
    5213: . body  - PetscGeom BODY object containing the FACE objects of interest.
    5214: - shell - PetscGeom SHELL object with FACEs of interest.

    5216:   Output Parameters:
    5217: + faces    - Array of PetscGeom FACE objects referenced by the PetscGeom SHELL object
    5218: - numFaces - Number of FACEs referenced by the PetscGeom SHELL object. Also the size of **faces array.

    5220:   Level: intermediate

    5222: .seealso: `DMPlexGetGeomModelBodies()`, `DMPlexGetGeomModelTUV()`, `DMPlexInflateToGeomModelUseTUV()`, `DMPlexInflateToGeomModelUseXYZ()`
    5223: @*/
    5224: PetscErrorCode DMPlexGetGeomModelShellFaces(DM dm, PetscGeom body, PetscGeom shell, PetscGeom **faces, PetscInt *numFaces) PeNS
    5225: {
    5226:   PetscFunctionBeginHot;
    5227:   #ifdef PETSC_HAVE_EGADS
    5228:   PetscContainer modelObj;
    5229:   PetscBool      islite = PETSC_FALSE;

    5231:   /* Determine which type of EGADS model is attached to the DM */
    5232:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
    5233:   if (!modelObj) {
    5234:     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
    5235:     islite = PETSC_TRUE;
    5236:   }

    5238:   if (islite) PetscCall(EGlite_getBodyTopos(body, shell, FACE, numFaces, faces));
    5239:   else PetscCall(EG_getBodyTopos(body, shell, FACE, numFaces, faces));
    5240:   #endif
    5241:   PetscFunctionReturn(PETSC_SUCCESS);
    5242: }

    5244: /*@C
    5245:   DMPlexGetGeomModelFaceLoops - Returns an array of `PetscGeom` LOOP objects attached to the referenced FACE geometric entity as well as the number of LOOPs.

    5247:   Collective

    5249:   Input Parameters:
    5250: + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
    5251: . body - PetscGeom BODY object containing the LOOP objects of interest.
    5252: - face - PetscGeom FACE object with LOOPs of interest.

    5254:   Output Parameters:
    5255: + loops    - Array of PetscGeom LOOP objects referenced by the PetscGeom FACE object
    5256: - numLoops - Number of LOOPs referenced by the PetscGeom FACE object. Also the size of **loops array.

    5258:   Level: intermediate

    5260: .seealso: `DMPlexGetGeomModelBodies()`, `DMPlexGetGeomModelTUV()`, `DMPlexInflateToGeomModelUseTUV()`, `DMPlexInflateToGeomModelUseXYZ()`
    5261: @*/
    5262: PetscErrorCode DMPlexGetGeomModelFaceLoops(DM dm, PetscGeom body, PetscGeom face, PetscGeom **loops, PetscInt *numLoops) PeNS
    5263: {
    5264:   PetscFunctionBeginHot;
    5265:   #ifdef PETSC_HAVE_EGADS
    5266:   PetscContainer modelObj;
    5267:   PetscBool      islite = PETSC_FALSE;

    5269:   /* Determine which type of EGADS model is attached to the DM */
    5270:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
    5271:   if (!modelObj) {
    5272:     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
    5273:     islite = PETSC_TRUE;
    5274:   }

    5276:   if (islite) PetscCall(EGlite_getBodyTopos(body, face, LOOP, numLoops, loops));
    5277:   else PetscCall(EG_getBodyTopos(body, face, LOOP, numLoops, loops));
    5278:   #endif
    5279:   PetscFunctionReturn(PETSC_SUCCESS);
    5280: }

    5282: /*@C
    5283:   DMPlexGetGeomModelFaceEdges - Returns an array of `PetscGeom` EDGE objects attached to the referenced FACE geometric entity as well as the number of EDGEs.

    5285:   Collective

    5287:   Input Parameters:
    5288: + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
    5289: . body - PetscGeom Body object containing the EDGE objects of interest.
    5290: - face - PetscGeom FACE object with EDGEs of interest.

    5292:   Output Parameters:
    5293: + edges    - Array of PetscGeom EDGE objects referenced by the PetscGeom FACE object
    5294: - numEdges - Number of EDGEs referenced by the PetscGeom FACE object. Also the size of **edges array.

    5296:   Level: intermediate

    5298: .seealso: `DMPlexGetGeomModelBodies()`, `DMPlexGetGeomModelTUV()`, `DMPlexInflateToGeomModelUseTUV()`, `DMPlexInflateToGeomModelUseXYZ()`
    5299: @*/
    5300: PetscErrorCode DMPlexGetGeomModelFaceEdges(DM dm, PetscGeom body, PetscGeom face, PetscGeom **edges, PetscInt *numEdges) PeNS
    5301: {
    5302:   PetscFunctionBeginHot;
    5303:   #ifdef PETSC_HAVE_EGADS
    5304:   PetscContainer modelObj;
    5305:   PetscBool      islite = PETSC_FALSE;

    5307:   /* Determine which type of EGADS model is attached to the DM */
    5308:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
    5309:   if (!modelObj) {
    5310:     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
    5311:     islite = PETSC_TRUE;
    5312:   }

    5314:   if (islite) PetscCall(EGlite_getBodyTopos(body, face, EDGE, numEdges, edges));
    5315:   else PetscCall(EG_getBodyTopos(body, face, EDGE, numEdges, edges));
    5316:   #endif
    5317:   PetscFunctionReturn(PETSC_SUCCESS);
    5318: }

    5320: /*@C
    5321:   DMPlexGetGeomModelBodyEdges - Returns an array of `PetscGeom` EDGE objects attached to the referenced BODY geometric entity as well as the number of EDGEs.

    5323:   Collective

    5325:   Input Parameters:
    5326: + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
    5327: - body - PetscGeom body object of interest.

    5329:   Output Parameters:
    5330: + edges    - Array of PetscGeom EDGE objects referenced by the PetscGeom BODY object
    5331: - numEdges - Number of EDGEs referenced by the PetscGeom BODY object. Also the size of **edges array.

    5333:   Level: intermediate

    5335: .seealso: `DMPlexGetGeomModelBodies()`, `DMPlexGetGeomModelTUV()`, `DMPlexInflateToGeomModelUseTUV()`, `DMPlexInflateToGeomModelUseXYZ()`
    5336: @*/
    5337: PetscErrorCode DMPlexGetGeomModelBodyEdges(DM dm, PetscGeom body, PetscGeom **edges, PetscInt *numEdges) PeNS
    5338: {
    5339:   PetscFunctionBeginHot;
    5340:   #ifdef PETSC_HAVE_EGADS
    5341:   PetscContainer modelObj;
    5342:   PetscBool      islite = PETSC_FALSE;

    5344:   /* Determine which type of EGADS model is attached to the DM */
    5345:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
    5346:   if (!modelObj) {
    5347:     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
    5348:     islite = PETSC_TRUE;
    5349:   }

    5351:   if (islite) PetscCall(EGlite_getBodyTopos(body, NULL, EDGE, numEdges, edges));
    5352:   else PetscCall(EG_getBodyTopos(body, NULL, EDGE, numEdges, edges));
    5353:   #endif
    5354:   PetscFunctionReturn(PETSC_SUCCESS);
    5355: }

    5357: /*@C
    5358:   DMPlexGetGeomModelBodyNodes - Returns an array of `PetscGeom` NODE objects attached to the referenced BODY geometric entity as well as the number of NODES.

    5360:   Collective

    5362:   Input Parameters:
    5363: + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
    5364: - body - PetscGeom body object of interest.

    5366:   Output Parameters:
    5367: + nodes    - Array of PetscGeom NODE objects referenced by the PetscGeom BODY object
    5368: - numNodes - Number of NODEs referenced by the PetscGeom BODY object. Also the size of **nodes array.

    5370:   Level: intermediate

    5372: .seealso: `DMPlexGetGeomModelBodies()`, `DMPlexGetGeomModelTUV()`, `DMPlexInflateToGeomModelUseTUV()`, `DMPlexInflateToGeomModelUseXYZ()`
    5373: @*/
    5374: PetscErrorCode DMPlexGetGeomModelBodyNodes(DM dm, PetscGeom body, PetscGeom **nodes, PetscInt *numNodes) PeNS
    5375: {
    5376:   PetscFunctionBeginHot;
    5377:   #ifdef PETSC_HAVE_EGADS
    5378:   PetscContainer modelObj;
    5379:   PetscBool      islite = PETSC_FALSE;

    5381:   /* Determine which type of EGADS model is attached to the DM */
    5382:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
    5383:   if (!modelObj) {
    5384:     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
    5385:     islite = PETSC_TRUE;
    5386:   }

    5388:   if (islite) PetscCall(EGlite_getBodyTopos(body, NULL, NODE, numNodes, nodes));
    5389:   else PetscCall(EG_getBodyTopos(body, NULL, NODE, numNodes, nodes));
    5390:   #endif
    5391:   PetscFunctionReturn(PETSC_SUCCESS);
    5392: }

    5394: /*@C
    5395:   DMPlexGetGeomModelEdgeNodes - Returns an array of `PetscGeom` NODE objects attached to the referenced EDGE geometric entity as well as the number of NODES.

    5397:   Collective

    5399:   Input Parameters:
    5400: + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
    5401: . body - PetscGeom body object containing the EDGE object of interest.
    5402: - edge - PetscGeom EDGE object with NODEs of interest.

    5404:   Output Parameters:
    5405: + nodes    - Array of PetscGeom NODE objects referenced by the PetscGeom EDGE object
    5406: - numNodes - Number of Nodes referenced by the PetscGeom EDGE object. Also the size of **nodes array.

    5408:   Level: intermediate

    5410: .seealso: `DMPlexGetGeomModelBodies()`, `DMPlexGetGeomModelTUV()`, `DMPlexInflateToGeomModelUseTUV()`, `DMPlexInflateToGeomModelUseXYZ()`
    5411: @*/
    5412: PetscErrorCode DMPlexGetGeomModelEdgeNodes(DM dm, PetscGeom body, PetscGeom edge, PetscGeom **nodes, PetscInt *numNodes) PeNS
    5413: {
    5414:   PetscFunctionBeginHot;
    5415:   #ifdef PETSC_HAVE_EGADS
    5416:   PetscContainer modelObj;
    5417:   PetscBool      islite = PETSC_FALSE;

    5419:   /* Determine which type of EGADS model is attached to the DM */
    5420:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
    5421:   if (!modelObj) {
    5422:     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
    5423:     islite = PETSC_TRUE;
    5424:   }

    5426:   if (islite) PetscCall(EGlite_getBodyTopos(body, edge, NODE, numNodes, nodes));
    5427:   else PetscCall(EG_getBodyTopos(body, edge, NODE, numNodes, nodes));
    5428:   #endif
    5429:   PetscFunctionReturn(PETSC_SUCCESS);
    5430: }

    5432: /*@C
    5433:   DMPlexGetGeomID - Returns ID number of the entity in the geometric (CAD) model

    5435:   Collective

    5437:   Input Parameters:
    5438: + dm      - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
    5439: . body    - PetscGeom body object containing the lower level entity the ID number is being requested.
    5440: - topoObj - PetscGeom SHELL, FACE, LOOP, EDGE, or NODE object for which ID number is being requested.

    5442:   Output Parameter:
    5443: . id - ID number of the entity

    5445:   Level: intermediate

    5447: .seealso: `DMPlexGetGeomModelBodies()`, `DMPlexGetGeomModelTUV()`, `DMPlexInflateToGeomModelUseTUV()`, `DMPlexInflateToGeomModelUseXYZ()`
    5448: @*/
    5449: PetscErrorCode DMPlexGetGeomID(DM dm, PetscGeom body, PetscGeom topoObj, PetscInt *id) PeNS
    5450: {
    5451:   PetscFunctionBeginHot;
    5452:   #ifdef PETSC_HAVE_EGADS
    5453:   PetscContainer modelObj;
    5454:   PetscBool      islite = PETSC_FALSE;
    5455:   int            topoID;

    5457:   /* Determine which type of EGADS model is attached to the DM */
    5458:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
    5459:   if (!modelObj) {
    5460:     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
    5461:     islite = PETSC_TRUE;
    5462:   }

    5464:   // Get Topology Object's ID
    5465:   if (islite) topoID = EGlite_indexBodyTopo(body, topoObj);
    5466:   else topoID = EG_indexBodyTopo(body, topoObj);

    5468:   *id = topoID;
    5469:   #endif
    5470:   PetscFunctionReturn(PETSC_SUCCESS);
    5471: }

    5473: /*@C
    5474:   DMPlexGetGeomObject - Returns Geometry Object using the objects ID in the geometric (CAD) model

    5476:   Collective

    5478:   Input Parameters:
    5479: + dm       - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
    5480: . body     - PetscGeom body object containing the lower level entity the referenced by the ID.
    5481: . geomType - Keyword SHELL, FACE, LOOP, EDGE, or NODE of the geometry type for which ID number is being requested.
    5482: - geomID   - ID number of the geometry entity being requested.

    5484:   Output Parameter:
    5485: . geomObj - Geometry Object referenced by the ID number requested.

    5487:   Level: intermediate

    5489: .seealso: `DMPlexGetGeomModelBodies()`, `DMPlexGetGeomModelTUV()`, `DMPlexInflateToGeomModelUseTUV()`, `DMPlexInflateToGeomModelUseXYZ()`
    5490: @*/
    5491: PetscErrorCode DMPlexGetGeomObject(DM dm, PetscGeom body, PetscInt geomType, PetscInt geomID, PetscGeom *geomObj) PeNS
    5492: {
    5493:   PetscFunctionBeginHot;
    5494:   #ifdef PETSC_HAVE_EGADS
    5495:   PetscContainer modelObj;
    5496:   PetscBool      islite = PETSC_FALSE;

    5498:   /* Determine which type of EGADS model is attached to the DM */
    5499:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
    5500:   if (!modelObj) {
    5501:     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
    5502:     islite = PETSC_TRUE;
    5503:   }

    5505:   // Get Topology Object's ID
    5506:   if (islite) PetscCall(EGlite_objectBodyTopo(body, geomType, geomID, geomObj));
    5507:   else PetscCall(EG_objectBodyTopo(body, geomType, geomID, geomObj));
    5508:   #endif
    5509:   PetscFunctionReturn(PETSC_SUCCESS);
    5510: }

    5512: /*@C
    5513:   DMPlexGetGeomFaceNumOfControlPoints - Returns the total number of Control Points (and associated Weights) defining a FACE of a Geometry

    5515:   Not collective

    5517:   Input Parameters:
    5518: + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
    5519: - face - PetscGeom FACE object

    5521:   Output Parameter:
    5522: . numCntrlPnts - Number of Control Points (and Weights) defining the FACE

    5524:   Level: intermediate

    5526: .seealso: `DMPlexGetGeomModelBodies()`, `DMPlexGetGeomModelTUV()`, `DMPlexInflateToGeomModelUseTUV()`, `DMPlexInflateToGeomModelUseXYZ()`
    5527: @*/
    5528: PetscErrorCode DMPlexGetGeomFaceNumOfControlPoints(DM dm, PetscGeom face, PetscInt *numCntrlPnts) PeNS
    5529: {
    5530:   PetscFunctionBeginHot;
    5531:   #ifdef PETSC_HAVE_EGADS
    5532:   PetscContainer modelObj;
    5533:   PetscBool      islite = PETSC_FALSE;
    5534:   PetscGeom      geom, gRef;
    5535:   PetscGeom     *lobjs;
    5536:   int            Nl, oclass, mtype, goclass, gmtype;
    5537:   int           *lsenses, *gpinfo;
    5538:   double        *gprv;

    5540:   /* Determine which type of EGADS model is attached to the DM */
    5541:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
    5542:   if (!modelObj) {
    5543:     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
    5544:     islite = PETSC_TRUE;
    5545:   }

    5547:   // Get Total Number of Control Points on FACE
    5548:   if (islite) {
    5549:     PetscCall(EGlite_getTopology(face, &geom, &oclass, &mtype, NULL, &Nl, &lobjs, &lsenses));
    5550:     PetscCall(EGlite_getGeometry(geom, &goclass, &gmtype, &gRef, &gpinfo, &gprv));
    5551:   } else {
    5552:     PetscCall(EG_getTopology(face, &geom, &oclass, &mtype, NULL, &Nl, &lobjs, &lsenses));
    5553:     PetscCall(EG_getGeometry(geom, &goclass, &gmtype, &gRef, &gpinfo, &gprv));
    5554:   }

    5556:   *numCntrlPnts = gpinfo[2] * gpinfo[5];
    5557:   #endif
    5558:   PetscFunctionReturn(PETSC_SUCCESS);
    5559: }

    5561: /*@C
    5562:   DMPlexGetGeomBodyMassProperties - Returns the Volume, Surface Area, Center of Gravity, and Inertia about the Body's Center of Gravity

    5564:   Not collective

    5566:   Input Parameters:
    5567: + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
    5568: - body - PetscGeom BODY object

    5570:   Output Parameters:
    5571: + volume           - Volume of the CAD Body attached to the DM Plex
    5572: . surfArea         - Surface Area of the CAD Body attached to the DM Plex
    5573: . centerOfGravity  - Array with the Center of Gravity coordinates of the CAD Body attached to the DM Plex [x, y, z]
    5574: . COGszie          - Size of centerOfGravity[] Array
    5575: . inertiaMatrixCOG - Array containing the Inertia about the Body's Center of Gravity [Ixx, Ixy, Ixz, Iyx, Iyy, Iyz, Izx, Izy, Izz]
    5576: - IMCOGsize        - Size of inertiaMatrixCOG[] Array

    5578:   Level: intermediate

    5580: .seealso: `DMPlexGetGeomModelBodies()`, `DMPlexGetGeomModelTUV()`, `DMPlexInflateToGeomModelUseTUV()`, `DMPlexInflateToGeomModelUseXYZ()`
    5581: @*/
    5582: PetscErrorCode DMPlexGetGeomBodyMassProperties(DM dm, PetscGeom body, PetscScalar *volume, PetscScalar *surfArea, PetscScalar **centerOfGravity, PetscInt *COGsize, PetscScalar **inertiaMatrixCOG, PetscInt *IMCOGsize) PeNS
    5583: {
    5584:   PetscFunctionBeginHot;
    5585:   #ifdef PETSC_HAVE_EGADS
    5586:   PetscContainer modelObj;
    5587:   PetscBool      islite = PETSC_FALSE;
    5588:   PetscScalar    geomData[14];

    5590:   /* Determine which type of EGADS model is attached to the DM */
    5591:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
    5592:   if (!modelObj) {
    5593:     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
    5594:     islite = PETSC_TRUE;
    5595:     PetscCheck(modelObj, PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot provide geometric mass properties for geometries defined by EGADSlite (.egadslite)! Please use another geometry file format STEP, IGES, EGADS or BRep");
    5596:   }

    5598:   if (islite) {
    5599:     PetscCall(PetscPrintf(PETSC_COMM_SELF, " WARNING!! This functionality is not supported for EGADSlite files. \n"));
    5600:     PetscCall(PetscPrintf(PETSC_COMM_SELF, " All returned values are equal to 0 \n"));
    5601:   } else PetscCall(EG_getMassProperties(body, geomData));

    5603:   PetscCall(PetscMalloc2(3, centerOfGravity, 9, inertiaMatrixCOG));

    5605:   if (!islite) {
    5606:     *volume   = geomData[0];
    5607:     *surfArea = geomData[1];
    5608:     for (int ii = 2; ii < 5; ++ii) (*centerOfGravity)[ii - 2] = geomData[ii];
    5609:     *COGsize = 3;
    5610:     for (int ii = 5; ii < 14; ++ii) (*inertiaMatrixCOG)[ii - 5] = geomData[ii];
    5611:     *IMCOGsize = 9;
    5612:   } else {
    5613:     *volume   = 0.;
    5614:     *surfArea = 0.;
    5615:     for (int ii = 2; ii < 5; ++ii) (*centerOfGravity)[ii - 2] = 0.;
    5616:     *COGsize = 0;
    5617:     for (int ii = 5; ii < 14; ++ii) (*inertiaMatrixCOG)[ii - 5] = 0.;
    5618:     *IMCOGsize = 0;
    5619:   }
    5620:   #endif
    5621:   PetscFunctionReturn(PETSC_SUCCESS);
    5622: }

    5624: PetscErrorCode DMPlexRestoreGeomBodyMassProperties(DM dm, PetscGeom body, PetscScalar *volume, PetscScalar *surfArea, PetscScalar **centerOfGravity, PetscInt *COGsize, PetscScalar **inertiaMatrixCOG, PetscInt *IMCOGsize) PeNS
    5625: {
    5626:   PetscFunctionBegin;
    5627:   PetscCall(PetscFree2(*centerOfGravity, *inertiaMatrixCOG));
    5628:   PetscFunctionReturn(PETSC_SUCCESS);
    5629: }

    5631: /*@C
    5632:   DMPlexFreeGeomObject - Frees PetscGeom Objects

    5634:   Not collective

    5636:   Input Parameters:
    5637: + dm      - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
    5638: - geomObj - PetscGeom object

    5640:   Level: intermediate

    5642: .seealso: `DMPlexGetGeomModelBodies()`, `DMPlexGetGeomModelTUV()`, `DMPlexInflateToGeomModelUseTUV()`, `DMPlexInflateToGeomModelUseXYZ()`
    5643: @*/
    5644: PetscErrorCode DMPlexFreeGeomObject(DM dm, PetscGeom *geomObj) PeNS
    5645: {
    5646:   PetscFunctionBeginHot;
    5647:   #ifdef PETSC_HAVE_EGADS
    5648:   PetscContainer modelObj;
    5649:   PetscBool      islite = PETSC_FALSE;

    5651:   /* Determine which type of EGADS model is attached to the DM */
    5652:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
    5653:   if (!modelObj) {
    5654:     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
    5655:     islite = PETSC_TRUE;
    5656:   }

    5658:   if (islite) EGlite_free(geomObj);
    5659:   else EG_free(geomObj);
    5660:   #endif
    5661:   PetscFunctionReturn(PETSC_SUCCESS);
    5662: }

    5664: /*@C
    5665:   DMPlexGetGeomCntrlPntAndWeightData - Gets Control Point and Associated Weight Data for the Geometry attached to the DMPlex

    5667:   Not collective

    5669:   Input Parameter:
    5670: . dm - The DMPlex object with an attached PetscContainer storing a CAD Geometry object

    5672:   Output Parameters:
    5673: + cpHashTable       - Hash Table containing the relationship between FACE ID and Control Point IDs.
    5674: . cpCoordDataLength - Length of cpCoordData Array.
    5675: . cpCoordData       - Array holding the Geometry Control Point Coordinate Data.
    5676: . maxNumEquiv       - Maximum Number of Equivalent Control Points (Control Points with the same coordinates but different IDs).
    5677: . cpEquiv           - Matrix with a size(Number of Control Points, Number or Control Points) which stores a value of 1.0 in locations where Control Points with different IDS (row or column) have the same coordinates
    5678: . wHashTable        - Hash Table containing the relationship between FACE ID and Control Point Weight.
    5679: . wDataLength       - Length of wData Array.
    5680: - wData             - Array holding the Weight for an associated Geometry Control Point.

    5682:   Note:
    5683:   Must Call DMPLexGeomDataAndGrads() before calling this function.

    5685:   Level: intermediate

    5687: .seealso: `DMPlexGetGeomModelBodies()`, `DMPlexGetGeomModelTUV()`, `DMPlexInflateToGeomModelUseTUV()`, `DMPlexInflateToGeomModelUseXYZ()`
    5688: @*/
    5689: PetscErrorCode DMPlexGetGeomCntrlPntAndWeightData(DM dm, PetscHMapI *cpHashTable, PetscInt *cpCoordDataLength, PetscScalar **cpCoordData, PetscInt *maxNumEquiv, Mat *cpEquiv, PetscHMapI *wHashTable, PetscInt *wDataLength, PetscScalar **wData) PeNS
    5690: {
    5691:   PetscContainer modelObj, cpHashTableObj, wHashTableObj, cpCoordDataLengthObj, wDataLengthObj, maxNumRelateObj;
    5692:   Vec            cntrlPtCoordsVec, cntrlPtWeightsVec;
    5693:   PetscInt      *cpCoordDataLengthPtr, *wDataLengthPtr, *maxNumEquivPtr;
    5694:   PetscHMapI     cpHashTableTemp, wHashTableTemp;

    5696:   PetscFunctionBeginHot;
    5697:   /* Determine which type of EGADS model is attached to the DM */
    5698:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
    5699:   if (!modelObj) PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));

    5701:   if (!modelObj) PetscFunctionReturn(PETSC_SUCCESS);

    5703:   // Look to see if DM has Container for Geometry Control Point Data
    5704:   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Hash Table", (PetscObject *)&cpHashTableObj));
    5705:   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Coordinates", (PetscObject *)&cntrlPtCoordsVec));
    5706:   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Coordinate Data Length", (PetscObject *)&cpCoordDataLengthObj));
    5707:   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weights Hash Table", (PetscObject *)&wHashTableObj));
    5708:   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight Data", (PetscObject *)&cntrlPtWeightsVec));
    5709:   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight Data Length", (PetscObject *)&wDataLengthObj));
    5710:   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Equivalency Matrix", (PetscObject *)cpEquiv));
    5711:   PetscCall(PetscObjectQuery((PetscObject)dm, "Maximum Number Control Point Equivalency", (PetscObject *)&maxNumRelateObj));

    5713:   // Get attached EGADS model Control Point and Weights Hash Tables and Data Arrays (pointer)
    5714:   PetscCall(PetscContainerGetPointer(cpHashTableObj, &cpHashTableTemp));
    5715:   PetscCall(PetscContainerGetPointer(cpCoordDataLengthObj, &cpCoordDataLengthPtr));
    5716:   PetscCall(PetscContainerGetPointer(wHashTableObj, &wHashTableTemp));
    5717:   PetscCall(PetscContainerGetPointer(wDataLengthObj, &wDataLengthPtr));
    5718:   PetscCall(PetscContainerGetPointer(maxNumRelateObj, &maxNumEquivPtr));

    5720:   *cpCoordDataLength = *cpCoordDataLengthPtr;
    5721:   *wDataLength       = *wDataLengthPtr;
    5722:   *maxNumEquiv       = *maxNumEquivPtr;
    5723:   *cpHashTable       = cpHashTableTemp;
    5724:   *wHashTable        = wHashTableTemp;
    5725:   PetscCall(VecGetArrayWrite(cntrlPtCoordsVec, cpCoordData));
    5726:   PetscCall(VecGetArrayWrite(cntrlPtWeightsVec, wData));
    5727:   PetscFunctionReturn(PETSC_SUCCESS);
    5728: }

    5730: PetscErrorCode DMPlexRestoreGeomCntrlPntAndWeightData(DM dm, PetscHMapI *cpHashTable, PetscInt *cpCoordDataLength, PetscScalar **cpCoordData, PetscInt *maxNumEquiv, Mat *cpEquiv, PetscHMapI *wHashTable, PetscInt *wDataLength, PetscScalar **wData)
    5731: {
    5732:   Vec cntrlPtCoordsVec, cntrlPtWeightsVec;

    5734:   PetscFunctionBeginHot;
    5735:   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Coordinates", (PetscObject *)&cntrlPtCoordsVec));
    5736:   PetscCall(VecRestoreArrayWrite(cntrlPtCoordsVec, cpCoordData));
    5737:   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight Data", (PetscObject *)&cntrlPtWeightsVec));
    5738:   PetscCall(VecRestoreArrayWrite(cntrlPtWeightsVec, wData));
    5739:   PetscFunctionReturn(PETSC_SUCCESS);
    5740: }

    5742: /*@C
    5743:   DMPlexGetGeomGradData - Gets Point, Surface and Volume Gradients with respect to changes in Control Points and their associated Weights for the Geometry attached to the DMPlex .

    5745:   Not collective

    5747:   Input Parameter:
    5748: . dm - The DMPlex object with an attached PetscContainer storing a CAD Geometry object

    5750:   Output Parameters:
    5751: + cpSurfGradHashTable - Hash Table Relating the Control Point ID to the the Row in the cpSurfGrad Matrix
    5752: . cpSurfGrad          - Matrix containing the Surface Gradient with respect to the Control Point Data. Data is ranged where the Row corresponds to Control Point ID and the Columns are associated with the Geometric FACE.
    5753: . cpArraySize         - The size of arrays gradSACP and gradVolCP and is equal to 3 * total number of Control Points in the Geometry
    5754: . gradSACP            - Array containing the Surface Area Gradient with respect to Control Point Data. Data is arranged by Control Point ID * 3 where 3 is for the coordinate dimension.
    5755: . gradVolCP           - Array containing the Volume Gradient with respect to Control Point Data. Data is arranged by Control Point ID * 3 where 3 is for the coordinate dimension.
    5756: . wArraySize          - The size of arrayws gradSAW and gradVolW and is equal to the total number of Control Points in the Geometry.
    5757: . gradSAW             - Array containing the Surface Area Gradient with respect to Control Point Weight. Data is arranged by Control Point ID.
    5758: - gradVolW            - Array containing the Volume Gradient with respect to Control Point Weight. Data is arranged by Control Point ID.

    5760:   Notes:
    5761:   Must Call `DMPLexGeomDataAndGrads()` before calling this function.

    5763:   `gradVolCP` and `gradVolW` are only available when `DMPlexGeomDataAndGrads()` is called with fullGeomGrad = PETSC_TRUE.

    5765:   Level: intermediate

    5767: .seealso: `DMPlexGeomDataAndGrads()`
    5768: @*/
    5769: PetscErrorCode DMPlexGetGeomGradData(DM dm, PetscHMapI *cpSurfGradHashTable, Mat *cpSurfGrad, PetscInt *cpArraySize, PetscScalar **gradSACP, PetscScalar **gradVolCP, PetscInt *wArraySize, PetscScalar **gradSAW, PetscScalar **gradVolW)
    5770: {
    5771:   PetscContainer modelObj, cpSurfGradHashTableObj, cpArraySizeObj, wArraySizeObj;
    5772:   Vec            gradSACPVec, gradVolCPVec, gradSAWVec, gradVolWVec;
    5773:   PetscInt      *cpArraySizePtr, *wArraySizePtr;
    5774:   PetscHMapI     cpSurfGradHashTableTemp;

    5776:   PetscFunctionBeginHot;
    5777:   /* Determine which type of EGADS model is attached to the DM */
    5778:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
    5779:   if (!modelObj) PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));

    5781:   if (!modelObj) PetscFunctionReturn(PETSC_SUCCESS);

    5783:   // Look to see if DM has Container for Geometry Control Point Data
    5784:   PetscCall(PetscObjectQuery((PetscObject)dm, "Surface Gradient Hash Table", (PetscObject *)&cpSurfGradHashTableObj));
    5785:   PetscCall(PetscObjectQuery((PetscObject)dm, "Surface Gradient Matrix", (PetscObject *)cpSurfGrad));
    5786:   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Coordinate Data Length", (PetscObject *)&cpArraySizeObj));
    5787:   PetscCall(PetscObjectQuery((PetscObject)dm, "Surface Area Control Point Gradient", (PetscObject *)&gradSACPVec));
    5788:   PetscCall(PetscObjectQuery((PetscObject)dm, "Volume Control Point Gradient", (PetscObject *)&gradVolCPVec));
    5789:   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight Data Length", (PetscObject *)&wArraySizeObj));
    5790:   PetscCall(PetscObjectQuery((PetscObject)dm, "Surface Area Weights Gradient", (PetscObject *)&gradSAWVec));
    5791:   PetscCall(PetscObjectQuery((PetscObject)dm, "Volume Weights Gradient", (PetscObject *)&gradVolWVec));

    5793:   // Get attached EGADS model Control Point and Weights Hash Tables and Data Arrays (pointer)
    5794:   if (cpSurfGradHashTableObj) {
    5795:     PetscCall(PetscContainerGetPointer(cpSurfGradHashTableObj, &cpSurfGradHashTableTemp));
    5796:     *cpSurfGradHashTable = cpSurfGradHashTableTemp;
    5797:   }

    5799:   if (cpArraySizeObj) {
    5800:     PetscCall(PetscContainerGetPointer(cpArraySizeObj, &cpArraySizePtr));
    5801:     *cpArraySize = *cpArraySizePtr;
    5802:   }

    5804:   if (gradSACPVec) PetscCall(VecGetArrayWrite(gradSACPVec, gradSACP));
    5805:   if (gradVolCPVec) PetscCall(VecGetArrayWrite(gradVolCPVec, gradVolCP));
    5806:   if (gradSAWVec) PetscCall(VecGetArrayWrite(gradSAWVec, gradSAW));
    5807:   if (gradVolWVec) PetscCall(VecGetArrayWrite(gradVolWVec, gradVolW));

    5809:   if (wArraySizeObj) {
    5810:     PetscCall(PetscContainerGetPointer(wArraySizeObj, &wArraySizePtr));
    5811:     *wArraySize = *wArraySizePtr;
    5812:   }
    5813:   PetscFunctionReturn(PETSC_SUCCESS);
    5814: }

    5816: PetscErrorCode DMPlexRestoreGeomGradData(DM dm, PetscHMapI *cpSurfGradHashTable, Mat *cpSurfGrad, PetscInt *cpArraySize, PetscScalar **gradSACP, PetscScalar **gradVolCP, PetscInt *wArraySize, PetscScalar **gradSAW, PetscScalar **gradVolW)
    5817: {
    5818:   Vec gradSACPVec, gradVolCPVec, gradSAWVec, gradVolWVec;

    5820:   PetscFunctionBegin;
    5821:   PetscCall(PetscObjectQuery((PetscObject)dm, "Surface Area Control Point Gradient", (PetscObject *)&gradSACPVec));
    5822:   PetscCall(PetscObjectQuery((PetscObject)dm, "Volume Control Point Gradient", (PetscObject *)&gradVolCPVec));
    5823:   PetscCall(PetscObjectQuery((PetscObject)dm, "Surface Area Weights Gradient", (PetscObject *)&gradSAWVec));
    5824:   PetscCall(PetscObjectQuery((PetscObject)dm, "Volume Weights Gradient", (PetscObject *)&gradVolWVec));

    5826:   if (gradSACPVec) PetscCall(VecRestoreArrayWrite(gradSACPVec, gradSACP));
    5827:   if (gradVolCPVec) PetscCall(VecRestoreArrayWrite(gradVolCPVec, gradVolCP));
    5828:   if (gradSAWVec) PetscCall(VecRestoreArrayWrite(gradSAWVec, gradSAW));
    5829:   if (gradVolWVec) PetscCall(VecRestoreArrayWrite(gradVolWVec, gradVolW));
    5830:   PetscFunctionReturn(PETSC_SUCCESS);
    5831: }

    5833: /*@C
    5834:   DMPlexGetGeomCntrlPntMaps - Gets arrays which maps Control Point IDs to their associated Geometry FACE, EDGE, and VERTEX.

    5836:   Not collective

    5838:   Input Parameter:
    5839: . dm - The DMPlex object with an attached PetscContainer storing a CAD Geometry object

    5841:   Output Parameters:
    5842: + numCntrlPnts            - Number of Control Points defining the Geometry attached to the DMPlex
    5843: . cntrlPntFaceMap         - Array containing the FACE ID for the Control Point. Array index corresponds to Control Point ID.
    5844: . cntrlPntWeightFaceMap   - Array containing the FACE ID for the Control Point Weight. Array index corresponds to Control Point ID.
    5845: . cntrlPntEdgeMap         - Array containing the EDGE ID for the Control Point. Array index corresponds to Control Point ID.
    5846: . cntrlPntWeightEdgeMap   - Array containing the EDGE ID for the Control Point Weight. Array index corresponds to Control Point ID.
    5847: . cntrlPntVertexMap       - Array containing the VERTEX ID for the Control Point. Array index corresponds to Control Point ID.
    5848: - cntrlPntWeightVertexMap - Array containing the VERTEX ID for the Control Point Weight. Array index corresponds to Control Point ID.

    5850:   Level: intermediate

    5852:   Note:
    5853:   Arrays are initialized to -1. Array elements with a -1 value indicates that the Control Point or Control Point Weight not associated with the referenced Geometric entity in the array name.

    5855: .seealso: `DMPlexGeomDataAndGrads()`
    5856: @*/
    5857: PetscErrorCode DMPlexGetGeomCntrlPntMaps(DM dm, PetscInt *numCntrlPnts, PetscInt **cntrlPntFaceMap, PetscInt **cntrlPntWeightFaceMap, PetscInt **cntrlPntEdgeMap, PetscInt **cntrlPntWeightEdgeMap, PetscInt **cntrlPntVertexMap, PetscInt **cntrlPntWeightVertexMap)
    5858: {
    5859:   PetscFunctionBeginHot;
    5860:   #ifdef PETSC_HAVE_EGADS
    5861:   PetscContainer modelObj, numCntrlPntsObj, cntrlPntFaceMapObj, cntrlPntWeightFaceMapObj, cntrlPntEdgeMapObj, cntrlPntWeightEdgeMapObj, cntrlPntVertexMapObj, cntrlPntWeightVertexMapObj;
    5862:   PetscInt      *numCntrlPntsPtr, *cntrlPntFaceMapPtr, *cntrlPntWeightFaceMapPtr, *cntrlPntEdgeMapPtr, *cntrlPntWeightEdgeMapPtr, *cntrlPntVertexMapPtr, *cntrlPntWeightVertexMapPtr;

    5864:   /* Determine which type of EGADS model is attached to the DM */
    5865:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
    5866:   if (!modelObj) PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));

    5868:   if (!modelObj) PetscFunctionReturn(PETSC_SUCCESS);

    5870:   // Look to see if DM has Container for Geometry Control Point Data
    5871:   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight Data Length", (PetscObject *)&numCntrlPntsObj));
    5872:   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point - Face Map", (PetscObject *)&cntrlPntFaceMapObj));
    5873:   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight - Face Map", (PetscObject *)&cntrlPntWeightFaceMapObj));
    5874:   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point - Edge Map", (PetscObject *)&cntrlPntEdgeMapObj));
    5875:   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight - Edge Map", (PetscObject *)&cntrlPntWeightEdgeMapObj));
    5876:   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point - Vertex Map", (PetscObject *)&cntrlPntVertexMapObj));
    5877:   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight - Vertex Map", (PetscObject *)&cntrlPntWeightVertexMapObj));

    5879:   // Get attached EGADS model Control Point and Weights Hash Tables and Data Arrays (pointer)
    5880:   if (numCntrlPntsObj) {
    5881:     PetscCall(PetscContainerGetPointer(numCntrlPntsObj, &numCntrlPntsPtr));
    5882:     *numCntrlPnts = *numCntrlPntsPtr;
    5883:   }

    5885:   if (cntrlPntFaceMapObj) {
    5886:     PetscCall(PetscContainerGetPointer(cntrlPntFaceMapObj, &cntrlPntFaceMapPtr));
    5887:     *cntrlPntFaceMap = cntrlPntFaceMapPtr;
    5888:   }

    5890:   if (cntrlPntWeightFaceMapObj) {
    5891:     PetscCall(PetscContainerGetPointer(cntrlPntWeightFaceMapObj, &cntrlPntWeightFaceMapPtr));
    5892:     *cntrlPntWeightFaceMap = cntrlPntWeightFaceMapPtr;
    5893:   }

    5895:   if (cntrlPntEdgeMapObj) {
    5896:     PetscCall(PetscContainerGetPointer(cntrlPntEdgeMapObj, &cntrlPntEdgeMapPtr));
    5897:     *cntrlPntEdgeMap = cntrlPntEdgeMapPtr;
    5898:   }

    5900:   if (cntrlPntWeightEdgeMapObj) {
    5901:     PetscCall(PetscContainerGetPointer(cntrlPntWeightEdgeMapObj, &cntrlPntWeightEdgeMapPtr));
    5902:     *cntrlPntWeightEdgeMap = cntrlPntWeightEdgeMapPtr;
    5903:   }

    5905:   if (cntrlPntVertexMapObj) {
    5906:     PetscCall(PetscContainerGetPointer(cntrlPntVertexMapObj, &cntrlPntVertexMapPtr));
    5907:     *cntrlPntVertexMap = cntrlPntVertexMapPtr;
    5908:   }

    5910:   if (cntrlPntWeightVertexMapObj) {
    5911:     PetscCall(PetscContainerGetPointer(cntrlPntWeightVertexMapObj, &cntrlPntWeightVertexMapPtr));
    5912:     *cntrlPntWeightVertexMap = cntrlPntWeightVertexMapPtr;
    5913:   }

    5915:   #endif
    5916:   PetscFunctionReturn(PETSC_SUCCESS);
    5917: }

    5919: #endif