Actual source code: plexegads.c

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

  4: #ifdef PETSC_HAVE_EGADS
  5:   #include <egads.h>
  6: #endif

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

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

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

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

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

 21: #ifdef PETSC_HAVE_EGADS
 22: PETSC_INTERN PetscErrorCode DMSnapToGeomModel_EGADS_Internal(DM, PetscInt, ego, PetscInt, PetscInt, PetscInt, const PetscScalar[], PetscScalar[]);
 23: PETSC_INTERN PetscErrorCode DMSnapToGeomModel_EGADSLite_Internal(DM, PetscInt, ego, PetscInt, PetscInt, PetscInt, const PetscScalar[], PetscScalar[]);

 25: PetscErrorCode DMSnapToGeomModel_EGADS_Internal(DM dm, PetscInt p, ego model, PetscInt bodyID, PetscInt faceID, PetscInt edgeID, const PetscScalar mcoords[], PetscScalar gcoords[])
 26: {
 27:   DM   cdm;
 28:   ego *bodies;
 29:   ego  geom, body, obj;
 30:   /* result has to hold derivatives, along with the value */
 31:   double       params[3], result[18], paramsV[16 * 3], resultV[16 * 3], range[4];
 32:   int          Nb, oclass, mtype, *senses, peri;
 33:   Vec          coordinatesLocal;
 34:   PetscScalar *coords = NULL;
 35:   PetscInt     Nv, v, Np = 0, pm;
 36:   PetscInt     dE, d;

 38:   PetscFunctionBeginHot;
 39:   PetscCall(DMGetCoordinateDM(dm, &cdm));
 40:   PetscCall(DMGetCoordinateDim(dm, &dE));
 41:   PetscCall(DMGetCoordinatesLocal(dm, &coordinatesLocal));
 42:   PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
 43:   PetscCheck(bodyID < Nb, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Body %" PetscInt_FMT " is not in [0, %d)", bodyID, Nb);
 44:   body = bodies[bodyID];

 46:   if (edgeID >= 0) {
 47:     PetscCall(EG_objectBodyTopo(body, EDGE, edgeID, &obj));
 48:     Np = 1;
 49:   } else if (faceID >= 0) {
 50:     PetscCall(EG_objectBodyTopo(body, FACE, faceID, &obj));
 51:     Np = 2;
 52:   } else {
 53:     for (d = 0; d < dE; ++d) gcoords[d] = mcoords[d];
 54:     PetscFunctionReturn(PETSC_SUCCESS);
 55:   }

 57:   /* Calculate parameters (t or u,v) for vertices */
 58:   PetscCall(DMPlexVecGetClosure(cdm, NULL, coordinatesLocal, p, &Nv, &coords));
 59:   Nv /= dE;
 60:   if (Nv == 1) {
 61:     PetscCall(DMPlexVecRestoreClosure(cdm, NULL, coordinatesLocal, p, &Nv, &coords));
 62:     for (d = 0; d < dE; ++d) gcoords[d] = mcoords[d];
 63:     PetscFunctionReturn(PETSC_SUCCESS);
 64:   }
 65:   PetscCheck(Nv <= 16, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot handle %" PetscInt_FMT " coordinates associated to point %" PetscInt_FMT, Nv, p);

 67:   /* Correct EGADSlite 2pi bug when calculating nearest point on Periodic Surfaces */
 68:   PetscCall(EG_getRange(obj, range, &peri));
 69:   for (v = 0; v < Nv; ++v) {
 70:     PetscCall(EG_invEvaluate(obj, &coords[v * dE], &paramsV[v * 3], &resultV[v * 3]));
 71:   #if 1
 72:     if (peri > 0) {
 73:       if (paramsV[v * 3 + 0] + 1.e-4 < range[0]) {
 74:         paramsV[v * 3 + 0] += 2. * PETSC_PI;
 75:       } else if (paramsV[v * 3 + 0] - 1.e-4 > range[1]) {
 76:         paramsV[v * 3 + 0] -= 2. * PETSC_PI;
 77:       }
 78:     }
 79:     if (peri > 1) {
 80:       if (paramsV[v * 3 + 1] + 1.e-4 < range[2]) {
 81:         paramsV[v * 3 + 1] += 2. * PETSC_PI;
 82:       } else if (paramsV[v * 3 + 1] - 1.e-4 > range[3]) {
 83:         paramsV[v * 3 + 1] -= 2. * PETSC_PI;
 84:       }
 85:     }
 86:   #endif
 87:   }
 88:   PetscCall(DMPlexVecRestoreClosure(cdm, NULL, coordinatesLocal, p, &Nv, &coords));
 89:   /* Calculate parameters (t or u,v) for new vertex at edge midpoint */
 90:   for (pm = 0; pm < Np; ++pm) {
 91:     params[pm] = 0.;
 92:     for (v = 0; v < Nv; ++v) params[pm] += paramsV[v * 3 + pm];
 93:     params[pm] /= Nv;
 94:   }
 95:   PetscCheck(!(params[0] < range[0]) && !(params[0] > range[1]), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Point %" PetscInt_FMT " had bad interpolation", p);
 96:   PetscCheck(Np <= 1 || (params[1] >= range[2] && params[1] <= range[3]), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Point %" PetscInt_FMT " had bad interpolation", p);
 97:   /* Put coordinates for new vertex in result[] */
 98:   PetscCall(EG_evaluate(obj, params, result));
 99:   for (d = 0; d < dE; ++d) gcoords[d] = result[d];
100:   PetscFunctionReturn(PETSC_SUCCESS);
101: }
102: #endif

104: PetscErrorCode DMSnapToGeomModel_EGADSLite(DM dm, PetscInt p, PetscInt dE, const PetscScalar mcoords[], PetscScalar gcoords[])
105: {
106:   PetscFunctionBeginHot;
107: #ifdef PETSC_HAVE_EGADS
108:   DMLabel        bodyLabel, faceLabel, edgeLabel;
109:   PetscInt       bodyID, faceID, edgeID;
110:   PetscContainer modelObj;
111:   ego            model;

113:   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
114:   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
115:   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
116:   PetscCheck(bodyLabel && faceLabel && edgeLabel, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "EGADSLite meshes must have body, face, and edge labels defined");
117:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSLite Model", (PetscObject *)&modelObj));
118:   PetscCheck(modelObj, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "EGADSLite mesh missing model object");

120:   PetscCall(PetscContainerGetPointer(modelObj, (void **)&model));
121:   PetscCall(DMLabelGetValue(bodyLabel, p, &bodyID));
122:   PetscCall(DMLabelGetValue(faceLabel, p, &faceID));
123:   PetscCall(DMLabelGetValue(edgeLabel, p, &edgeID));
124:   /* Allows for "Connective" Plex Edges present in models with multiple non-touching Entities */
125:   if (bodyID < 0) {
126:     for (PetscInt d = 0; d < dE; ++d) gcoords[d] = mcoords[d];
127:     PetscFunctionReturn(PETSC_SUCCESS);
128:   }
129:   PetscCall(DMSnapToGeomModel_EGADSLite_Internal(dm, p, model, bodyID, faceID, edgeID, mcoords, gcoords));
130: #endif
131:   PetscFunctionReturn(PETSC_SUCCESS);
132: }

134: PetscErrorCode DMSnapToGeomModel_EGADS(DM dm, PetscInt p, PetscInt dE, const PetscScalar mcoords[], PetscScalar gcoords[])
135: {
136:   PetscFunctionBeginHot;
137: #ifdef PETSC_HAVE_EGADS
138:   DMLabel        bodyLabel, faceLabel, edgeLabel;
139:   PetscInt       bodyID, faceID, edgeID;
140:   PetscContainer modelObj;
141:   ego            model;

143:   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
144:   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
145:   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
146:   PetscCheck(bodyLabel && faceLabel && edgeLabel, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "EGADS meshes must have body, face, and edge labels defined");
147:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
148:   PetscCheck(modelObj, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "EGADS mesh missing model object");

150:   PetscCall(PetscContainerGetPointer(modelObj, (void **)&model));
151:   PetscCall(DMLabelGetValue(bodyLabel, p, &bodyID));
152:   PetscCall(DMLabelGetValue(faceLabel, p, &faceID));
153:   PetscCall(DMLabelGetValue(edgeLabel, p, &edgeID));
154:   /* Allows for "Connective" Plex Edges present in models with multiple non-touching Entities */
155:   if (bodyID < 0) {
156:     for (PetscInt d = 0; d < dE; ++d) gcoords[d] = mcoords[d];
157:     PetscFunctionReturn(PETSC_SUCCESS);
158:   }
159:   PetscCall(DMSnapToGeomModel_EGADS_Internal(dm, p, model, bodyID, faceID, edgeID, mcoords, gcoords));
160: #endif
161:   PetscFunctionReturn(PETSC_SUCCESS);
162: }

164: #if defined(PETSC_HAVE_EGADS)
165: static PetscErrorCode DMPlexEGADSPrintModel_Internal(ego model)
166: {
167:   ego geom, *bodies, *objs, *nobjs, *mobjs, *lobjs;
168:   int oclass, mtype, *senses;
169:   int Nb, b;

171:   PetscFunctionBeginUser;
172:   /* test bodyTopo functions */
173:   PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
174:   PetscCall(PetscPrintf(PETSC_COMM_SELF, " Number of BODIES (nbodies): %d \n", Nb));

176:   for (b = 0; b < Nb; ++b) {
177:     ego body = bodies[b];
178:     int id, Nsh, Nf, Nl, l, Ne, e, Nv, v;

180:     /* Output Basic Model Topology */
181:     PetscCall(EG_getBodyTopos(body, NULL, SHELL, &Nsh, &objs));
182:     PetscCall(PetscPrintf(PETSC_COMM_SELF, "   Number of SHELLS: %d \n", Nsh));
183:     EG_free(objs);

185:     PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &objs));
186:     PetscCall(PetscPrintf(PETSC_COMM_SELF, "   Number of FACES: %d \n", Nf));
187:     EG_free(objs);

189:     PetscCall(EG_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
190:     PetscCall(PetscPrintf(PETSC_COMM_SELF, "   Number of LOOPS: %d \n", Nl));

192:     PetscCall(EG_getBodyTopos(body, NULL, EDGE, &Ne, &objs));
193:     PetscCall(PetscPrintf(PETSC_COMM_SELF, "   Number of EDGES: %d \n", Ne));
194:     EG_free(objs);

196:     PetscCall(EG_getBodyTopos(body, NULL, NODE, &Nv, &objs));
197:     PetscCall(PetscPrintf(PETSC_COMM_SELF, "   Number of NODES: %d \n", Nv));
198:     EG_free(objs);

200:     for (l = 0; l < Nl; ++l) {
201:       ego loop = lobjs[l];

203:       id = EG_indexBodyTopo(body, loop);
204:       PetscCall(PetscPrintf(PETSC_COMM_SELF, "          LOOP ID: %d\n", id));

206:       /* Get EDGE info which associated with the current LOOP */
207:       PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));

209:       for (e = 0; e < Ne; ++e) {
210:         ego    edge      = objs[e];
211:         double range[4]  = {0., 0., 0., 0.};
212:         double point[3]  = {0., 0., 0.};
213:         double params[3] = {0., 0., 0.};
214:         double result[18];
215:         int    peri;

217:         id = EG_indexBodyTopo(body, edge);
218:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "            EDGE ID: %d (%d)\n", id, e));

220:         PetscCall(EG_getRange(edge, range, &peri));
221:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Range = %lf, %lf, %lf, %lf \n", range[0], range[1], range[2], range[3]));

223:         /* Get NODE info which associated with the current EDGE */
224:         PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
225:         if (mtype == DEGENERATE) {
226:           PetscCall(PetscPrintf(PETSC_COMM_SELF, "  EDGE %d is DEGENERATE \n", id));
227:         } else {
228:           params[0] = range[0];
229:           PetscCall(EG_evaluate(edge, params, result));
230:           PetscCall(PetscPrintf(PETSC_COMM_SELF, "   between (%lf, %lf, %lf)", result[0], result[1], result[2]));
231:           params[0] = range[1];
232:           PetscCall(EG_evaluate(edge, params, result));
233:           PetscCall(PetscPrintf(PETSC_COMM_SELF, " and (%lf, %lf, %lf)\n", result[0], result[1], result[2]));
234:         }

236:         for (v = 0; v < Nv; ++v) {
237:           ego    vertex = nobjs[v];
238:           double limits[4];
239:           int    dummy;

241:           PetscCall(EG_getTopology(vertex, &geom, &oclass, &mtype, limits, &dummy, &mobjs, &senses));
242:           id = EG_indexBodyTopo(body, vertex);
243:           PetscCall(PetscPrintf(PETSC_COMM_SELF, "              NODE ID: %d \n", id));
244:           PetscCall(PetscPrintf(PETSC_COMM_SELF, "                 (x, y, z) = (%lf, %lf, %lf) \n", limits[0], limits[1], limits[2]));

246:           point[0] = point[0] + limits[0];
247:           point[1] = point[1] + limits[1];
248:           point[2] = point[2] + limits[2];
249:         }
250:       }
251:     }
252:     EG_free(lobjs);
253:   }
254:   PetscFunctionReturn(PETSC_SUCCESS);
255: }

257: static PetscErrorCode DMPlexEGADSDestroy_Private(void **context)
258: {
259:   if (context) EG_close((ego)*context);
260:   return 0;
261: }

263: static PetscErrorCode DMPlexCreateEGADS_Internal(MPI_Comm comm, ego context, ego model, DM *newdm)
264: {
265:   DMLabel  bodyLabel, faceLabel, edgeLabel, vertexLabel;
266:   PetscInt cStart, cEnd, c;
267:   /* EGADSLite variables */
268:   ego geom, *bodies, *objs, *nobjs, *mobjs, *lobjs;
269:   int oclass, mtype, nbodies, *senses;
270:   int b;
271:   /* PETSc variables */
272:   DM          dm;
273:   PetscHMapI  edgeMap = NULL;
274:   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;
275:   PetscInt   *cells = NULL, *cone = NULL;
276:   PetscReal  *coords = NULL;
277:   PetscMPIInt rank;

279:   PetscFunctionBegin;
280:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
281:   if (rank == 0) {
282:     const PetscInt debug = 0;

284:     /* ---------------------------------------------------------------------------------------------------
285:     Generate Petsc Plex
286:       Get all Nodes in model, record coordinates in a correctly formatted array
287:       Cycle through bodies, cycle through loops, recorde NODE IDs in a correctly formatted array
288:       We need to uniformly refine the initial geometry to guarantee a valid mesh
289:     */

291:     /* Calculate cell and vertex sizes */
292:     PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &nbodies, &bodies, &senses));
293:     PetscCall(PetscHMapICreate(&edgeMap));
294:     numEdges = 0;
295:     for (b = 0; b < nbodies; ++b) {
296:       ego body = bodies[b];
297:       int id, Nl, l, Nv, v;

299:       PetscCall(EG_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
300:       for (l = 0; l < Nl; ++l) {
301:         ego loop = lobjs[l];
302:         int Ner  = 0, Ne, e, Nc;

304:         PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));
305:         for (e = 0; e < Ne; ++e) {
306:           ego           edge = objs[e];
307:           int           Nv, id;
308:           PetscHashIter iter;
309:           PetscBool     found;

311:           PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
312:           if (mtype == DEGENERATE) continue;
313:           id = EG_indexBodyTopo(body, edge);
314:           PetscCall(PetscHMapIFind(edgeMap, id - 1, &iter, &found));
315:           if (!found) PetscCall(PetscHMapISet(edgeMap, id - 1, numEdges++));
316:           ++Ner;
317:         }
318:         if (Ner == 2) {
319:           Nc = 2;
320:         } else if (Ner == 3) {
321:           Nc = 4;
322:         } else if (Ner == 4) {
323:           Nc = 8;
324:           ++numQuads;
325:         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot support loop with %d edges", Ner);
326:         numCells += Nc;
327:         newCells += Nc - 1;
328:         maxCorners = PetscMax(Ner * 2 + 1, maxCorners);
329:       }
330:       PetscCall(EG_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
331:       for (v = 0; v < Nv; ++v) {
332:         ego vertex = nobjs[v];

334:         id = EG_indexBodyTopo(body, vertex);
335:         /* TODO: Instead of assuming contiguous ids, we could use a hash table */
336:         numVertices = PetscMax(id, numVertices);
337:       }
338:       EG_free(lobjs);
339:       EG_free(nobjs);
340:     }
341:     PetscCall(PetscHMapIGetSize(edgeMap, &numEdges));
342:     newVertices = numEdges + numQuads;
343:     numVertices += newVertices;

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

350:     /* Get vertex coordinates */
351:     for (b = 0; b < nbodies; ++b) {
352:       ego body = bodies[b];
353:       int id, Nv, v;

355:       PetscCall(EG_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
356:       for (v = 0; v < Nv; ++v) {
357:         ego    vertex = nobjs[v];
358:         double limits[4];
359:         int    dummy;

361:         PetscCall(EG_getTopology(vertex, &geom, &oclass, &mtype, limits, &dummy, &mobjs, &senses));
362:         id                          = EG_indexBodyTopo(body, vertex);
363:         coords[(id - 1) * cdim + 0] = limits[0];
364:         coords[(id - 1) * cdim + 1] = limits[1];
365:         coords[(id - 1) * cdim + 2] = limits[2];
366:       }
367:       EG_free(nobjs);
368:     }
369:     PetscCall(PetscHMapIClear(edgeMap));
370:     fOff     = numVertices - newVertices + numEdges;
371:     numEdges = 0;
372:     numQuads = 0;
373:     for (b = 0; b < nbodies; ++b) {
374:       ego body = bodies[b];
375:       int Nl, l;

377:       PetscCall(EG_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
378:       for (l = 0; l < Nl; ++l) {
379:         ego loop = lobjs[l];
380:         int lid, Ner = 0, Ne, e;

382:         lid = EG_indexBodyTopo(body, loop);
383:         PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));
384:         for (e = 0; e < Ne; ++e) {
385:           ego           edge = objs[e];
386:           int           eid, Nv;
387:           PetscHashIter iter;
388:           PetscBool     found;

390:           PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
391:           if (mtype == DEGENERATE) continue;
392:           ++Ner;
393:           eid = EG_indexBodyTopo(body, edge);
394:           PetscCall(PetscHMapIFind(edgeMap, eid - 1, &iter, &found));
395:           if (!found) {
396:             PetscInt v = numVertices - newVertices + numEdges;
397:             double   range[4], params[3] = {0., 0., 0.}, result[18];
398:             int      periodic[2];

400:             PetscCall(PetscHMapISet(edgeMap, eid - 1, numEdges++));
401:             PetscCall(EG_getRange(edge, range, periodic));
402:             params[0] = 0.5 * (range[0] + range[1]);
403:             PetscCall(EG_evaluate(edge, params, result));
404:             coords[v * cdim + 0] = result[0];
405:             coords[v * cdim + 1] = result[1];
406:             coords[v * cdim + 2] = result[2];
407:           }
408:         }
409:         if (Ner == 4) {
410:           PetscInt v = fOff + numQuads++;
411:           ego     *fobjs, face;
412:           double   range[4], params[3] = {0., 0., 0.}, result[18];
413:           int      Nf, fid, periodic[2];

415:           PetscCall(EG_getBodyTopos(body, loop, FACE, &Nf, &fobjs));
416:           face = fobjs[0];
417:           fid  = EG_indexBodyTopo(body, face);
418:           PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Loop %d has %d faces, instead of 1 (%d)", lid - 1, Nf, fid);
419:           PetscCall(EG_getRange(face, range, periodic));
420:           params[0] = 0.5 * (range[0] + range[1]);
421:           params[1] = 0.5 * (range[2] + range[3]);
422:           PetscCall(EG_evaluate(face, params, result));
423:           coords[v * cdim + 0] = result[0];
424:           coords[v * cdim + 1] = result[1];
425:           coords[v * cdim + 2] = result[2];
426:         }
427:       }
428:     }
429:     PetscCheck(numEdges + numQuads == newVertices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of new vertices %" PetscInt_FMT " != %" PetscInt_FMT " previous count", numEdges + numQuads, newVertices);

431:     /* Get cell vertices by traversing loops */
432:     numQuads = 0;
433:     cOff     = 0;
434:     for (b = 0; b < nbodies; ++b) {
435:       ego body = bodies[b];
436:       int id, Nl, l;

438:       PetscCall(EG_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
439:       for (l = 0; l < Nl; ++l) {
440:         ego loop = lobjs[l];
441:         int lid, Ner = 0, Ne, e, nc = 0, c, Nt, t;

443:         lid = EG_indexBodyTopo(body, loop);
444:         PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));

446:         for (e = 0; e < Ne; ++e) {
447:           ego edge = objs[e];
448:           int points[3];
449:           int eid, Nv, v, tmp;

451:           eid = EG_indexBodyTopo(body, edge);
452:           PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
453:           if (mtype == DEGENERATE) continue;
454:           else ++Ner;
455:           PetscCheck(Nv == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Edge %d has %d vertices != 2", eid, Nv);

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

460:             id            = EG_indexBodyTopo(body, vertex);
461:             points[v * 2] = id - 1;
462:           }
463:           {
464:             PetscInt edgeNum;

466:             PetscCall(PetscHMapIGet(edgeMap, eid - 1, &edgeNum));
467:             points[1] = numVertices - newVertices + edgeNum;
468:           }
469:           /* EGADS loops are not oriented, but seem to be in order, so we must piece them together */
470:           if (!nc) {
471:             for (v = 0; v < Nv + 1; ++v) cone[nc++] = points[v];
472:           } else {
473:             if (cone[nc - 1] == points[0]) {
474:               cone[nc++] = points[1];
475:               if (cone[0] != points[2]) cone[nc++] = points[2];
476:             } else if (cone[nc - 1] == points[2]) {
477:               cone[nc++] = points[1];
478:               if (cone[0] != points[0]) cone[nc++] = points[0];
479:             } else if (cone[nc - 3] == points[0]) {
480:               tmp          = cone[nc - 3];
481:               cone[nc - 3] = cone[nc - 1];
482:               cone[nc - 1] = tmp;
483:               cone[nc++]   = points[1];
484:               if (cone[0] != points[2]) cone[nc++] = points[2];
485:             } else if (cone[nc - 3] == points[2]) {
486:               tmp          = cone[nc - 3];
487:               cone[nc - 3] = cone[nc - 1];
488:               cone[nc - 1] = tmp;
489:               cone[nc++]   = points[1];
490:               if (cone[0] != points[0]) cone[nc++] = points[0];
491:             } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Edge %d does not match its predecessor", eid);
492:           }
493:         }
494:         PetscCheck(nc == 2 * Ner, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Number of corners %" PetscInt_FMT " != %" PetscInt_FMT, nc, 2 * Ner);
495:         if (Ner == 4) cone[nc++] = numVertices - newVertices + numEdges + numQuads++;
496:         PetscCheck(nc <= maxCorners, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Number of corners %" PetscInt_FMT " > %" PetscInt_FMT " max", nc, maxCorners);
497:         /* Triangulate the loop */
498:         switch (Ner) {
499:         case 2: /* Bi-Segment -> 2 triangles */
500:           Nt                           = 2;
501:           cells[cOff * numCorners + 0] = cone[0];
502:           cells[cOff * numCorners + 1] = cone[1];
503:           cells[cOff * numCorners + 2] = cone[2];
504:           ++cOff;
505:           cells[cOff * numCorners + 0] = cone[0];
506:           cells[cOff * numCorners + 1] = cone[2];
507:           cells[cOff * numCorners + 2] = cone[3];
508:           ++cOff;
509:           break;
510:         case 3: /* Triangle   -> 4 triangles */
511:           Nt                           = 4;
512:           cells[cOff * numCorners + 0] = cone[0];
513:           cells[cOff * numCorners + 1] = cone[1];
514:           cells[cOff * numCorners + 2] = cone[5];
515:           ++cOff;
516:           cells[cOff * numCorners + 0] = cone[1];
517:           cells[cOff * numCorners + 1] = cone[2];
518:           cells[cOff * numCorners + 2] = cone[3];
519:           ++cOff;
520:           cells[cOff * numCorners + 0] = cone[5];
521:           cells[cOff * numCorners + 1] = cone[3];
522:           cells[cOff * numCorners + 2] = cone[4];
523:           ++cOff;
524:           cells[cOff * numCorners + 0] = cone[1];
525:           cells[cOff * numCorners + 1] = cone[3];
526:           cells[cOff * numCorners + 2] = cone[5];
527:           ++cOff;
528:           break;
529:         case 4: /* Quad       -> 8 triangles */
530:           Nt                           = 8;
531:           cells[cOff * numCorners + 0] = cone[0];
532:           cells[cOff * numCorners + 1] = cone[1];
533:           cells[cOff * numCorners + 2] = cone[7];
534:           ++cOff;
535:           cells[cOff * numCorners + 0] = cone[1];
536:           cells[cOff * numCorners + 1] = cone[2];
537:           cells[cOff * numCorners + 2] = cone[3];
538:           ++cOff;
539:           cells[cOff * numCorners + 0] = cone[3];
540:           cells[cOff * numCorners + 1] = cone[4];
541:           cells[cOff * numCorners + 2] = cone[5];
542:           ++cOff;
543:           cells[cOff * numCorners + 0] = cone[5];
544:           cells[cOff * numCorners + 1] = cone[6];
545:           cells[cOff * numCorners + 2] = cone[7];
546:           ++cOff;
547:           cells[cOff * numCorners + 0] = cone[8];
548:           cells[cOff * numCorners + 1] = cone[1];
549:           cells[cOff * numCorners + 2] = cone[3];
550:           ++cOff;
551:           cells[cOff * numCorners + 0] = cone[8];
552:           cells[cOff * numCorners + 1] = cone[3];
553:           cells[cOff * numCorners + 2] = cone[5];
554:           ++cOff;
555:           cells[cOff * numCorners + 0] = cone[8];
556:           cells[cOff * numCorners + 1] = cone[5];
557:           cells[cOff * numCorners + 2] = cone[7];
558:           ++cOff;
559:           cells[cOff * numCorners + 0] = cone[8];
560:           cells[cOff * numCorners + 1] = cone[7];
561:           cells[cOff * numCorners + 2] = cone[1];
562:           ++cOff;
563:           break;
564:         default:
565:           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Loop %d has %d edges, which we do not support", lid, Ner);
566:         }
567:         if (debug) {
568:           for (t = 0; t < Nt; ++t) {
569:             PetscCall(PetscPrintf(PETSC_COMM_SELF, "  LOOP Corner NODEs Triangle %" PetscInt_FMT " (", t));
570:             for (c = 0; c < numCorners; ++c) {
571:               if (c > 0) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
572:               PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT, cells[(cOff - Nt + t) * numCorners + c]));
573:             }
574:             PetscCall(PetscPrintf(PETSC_COMM_SELF, ")\n"));
575:           }
576:         }
577:       }
578:       EG_free(lobjs);
579:     }
580:   }
581:   PetscCheck(cOff == numCells, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Count of total cells %" PetscInt_FMT " != %" PetscInt_FMT " previous count", cOff, numCells);
582:   PetscCall(DMPlexCreateFromCellListPetsc(PETSC_COMM_WORLD, dim, numCells, numVertices, numCorners, PETSC_TRUE, cells, cdim, coords, &dm));
583:   PetscCall(PetscFree3(coords, cells, cone));
584:   PetscCall(PetscInfo(dm, " Total Number of Unique Cells    = %" PetscInt_FMT " (%" PetscInt_FMT ")\n", numCells, newCells));
585:   PetscCall(PetscInfo(dm, " Total Number of Unique Vertices = %" PetscInt_FMT " (%" PetscInt_FMT ")\n", numVertices, newVertices));
586:   /* Embed EGADS model in DM */
587:   {
588:     PetscContainer modelObj, contextObj;

590:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &modelObj));
591:     PetscCall(PetscContainerSetPointer(modelObj, model));
592:     PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Model", (PetscObject)modelObj));
593:     PetscCall(PetscContainerDestroy(&modelObj));

595:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &contextObj));
596:     PetscCall(PetscContainerSetPointer(contextObj, context));
597:     PetscCall(PetscContainerSetCtxDestroy(contextObj, DMPlexEGADSDestroy_Private));
598:     PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Context", (PetscObject)contextObj));
599:     PetscCall(PetscContainerDestroy(&contextObj));
600:   }
601:   /* Label points */
602:   PetscCall(DMCreateLabel(dm, "EGADS Body ID"));
603:   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
604:   PetscCall(DMCreateLabel(dm, "EGADS Face ID"));
605:   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
606:   PetscCall(DMCreateLabel(dm, "EGADS Edge ID"));
607:   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
608:   PetscCall(DMCreateLabel(dm, "EGADS Vertex ID"));
609:   PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));
610:   cOff = 0;
611:   for (b = 0; b < nbodies; ++b) {
612:     ego body = bodies[b];
613:     int id, Nl, l;

615:     PetscCall(EG_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
616:     for (l = 0; l < Nl; ++l) {
617:       ego  loop = lobjs[l];
618:       ego *fobjs;
619:       int  lid, Nf, fid, Ner = 0, Ne, e, Nt = 0, t;

621:       lid = EG_indexBodyTopo(body, loop);
622:       PetscCall(EG_getBodyTopos(body, loop, FACE, &Nf, &fobjs));
623:       PetscCheck(Nf <= 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Loop %d has %d > 1 faces, which is not supported", lid, Nf);
624:       fid = EG_indexBodyTopo(body, fobjs[0]);
625:       EG_free(fobjs);
626:       PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));
627:       for (e = 0; e < Ne; ++e) {
628:         ego             edge = objs[e];
629:         int             eid, Nv, v;
630:         PetscInt        points[3], support[2], numEdges, edgeNum;
631:         const PetscInt *edges;

633:         eid = EG_indexBodyTopo(body, edge);
634:         PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
635:         if (mtype == DEGENERATE) continue;
636:         else ++Ner;
637:         for (v = 0; v < Nv; ++v) {
638:           ego vertex = nobjs[v];

640:           id = EG_indexBodyTopo(body, vertex);
641:           PetscCall(DMLabelSetValue(edgeLabel, numCells + id - 1, eid));
642:           points[v * 2] = numCells + id - 1;
643:         }
644:         PetscCall(PetscHMapIGet(edgeMap, eid - 1, &edgeNum));
645:         points[1] = numCells + numVertices - newVertices + edgeNum;

647:         PetscCall(DMLabelSetValue(edgeLabel, points[1], eid));
648:         support[0] = points[0];
649:         support[1] = points[1];
650:         PetscCall(DMPlexGetJoin(dm, 2, support, &numEdges, &edges));
651:         PetscCheck(numEdges == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Vertices (%" PetscInt_FMT ", %" PetscInt_FMT ") should only bound 1 edge, not %" PetscInt_FMT, support[0], support[1], numEdges);
652:         PetscCall(DMLabelSetValue(edgeLabel, edges[0], eid));
653:         PetscCall(DMPlexRestoreJoin(dm, 2, support, &numEdges, &edges));
654:         support[0] = points[1];
655:         support[1] = points[2];
656:         PetscCall(DMPlexGetJoin(dm, 2, support, &numEdges, &edges));
657:         PetscCheck(numEdges == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Vertices (%" PetscInt_FMT ", %" PetscInt_FMT ") should only bound 1 edge, not %" PetscInt_FMT, support[0], support[1], numEdges);
658:         PetscCall(DMLabelSetValue(edgeLabel, edges[0], eid));
659:         PetscCall(DMPlexRestoreJoin(dm, 2, support, &numEdges, &edges));
660:       }
661:       switch (Ner) {
662:       case 2:
663:         Nt = 2;
664:         break;
665:       case 3:
666:         Nt = 4;
667:         break;
668:       case 4:
669:         Nt = 8;
670:         break;
671:       default:
672:         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Loop with %d edges is unsupported", Ner);
673:       }
674:       for (t = 0; t < Nt; ++t) {
675:         PetscCall(DMLabelSetValue(bodyLabel, cOff + t, b));
676:         PetscCall(DMLabelSetValue(faceLabel, cOff + t, fid));
677:       }
678:       cOff += Nt;
679:     }
680:     EG_free(lobjs);
681:   }
682:   PetscCall(PetscHMapIDestroy(&edgeMap));
683:   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
684:   for (c = cStart; c < cEnd; ++c) {
685:     PetscInt *closure = NULL;
686:     PetscInt  clSize, cl, bval, fval;

688:     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
689:     PetscCall(DMLabelGetValue(bodyLabel, c, &bval));
690:     PetscCall(DMLabelGetValue(faceLabel, c, &fval));
691:     for (cl = 0; cl < clSize * 2; cl += 2) {
692:       PetscCall(DMLabelSetValue(bodyLabel, closure[cl], bval));
693:       PetscCall(DMLabelSetValue(faceLabel, closure[cl], fval));
694:     }
695:     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
696:   }
697:   *newdm = dm;
698:   PetscFunctionReturn(PETSC_SUCCESS);
699: }

701: static PetscErrorCode DMPlexCreateEGADS(MPI_Comm comm, ego context, ego model, DM *newdm)
702: {
703:   DMLabel bodyLabel, faceLabel, edgeLabel, vertexLabel;
704:   // EGADS/EGADSLite variables
705:   ego geom, *bodies, *mobjs, *fobjs, *lobjs, *eobjs, *nobjs;
706:   ego topRef, prev, next;
707:   int oclass, mtype, nbodies, *senses, *lSenses, *eSenses;
708:   int b;
709:   // PETSc variables
710:   DM              dm;
711:   PetscHMapI      edgeMap = NULL, bodyIndexMap = NULL, bodyVertexMap = NULL, bodyEdgeMap = NULL, bodyFaceMap = NULL, bodyEdgeGlobalMap = NULL;
712:   PetscInt        dim = -1, cdim = -1, numCorners = 0, numVertices = 0, numEdges = 0, numFaces = 0, numCells = 0, edgeCntr = 0;
713:   PetscInt        cellCntr = 0, numPoints = 0;
714:   PetscInt       *cells  = NULL;
715:   const PetscInt *cone   = NULL;
716:   PetscReal      *coords = NULL;
717:   PetscMPIInt     rank;

719:   PetscFunctionBeginUser;
720:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
721:   if (rank == 0) {
722:     // ---------------------------------------------------------------------------------------------------
723:     // Generate Petsc Plex
724:     //  Get all Nodes in model, record coordinates in a correctly formatted array
725:     //  Cycle through bodies, cycle through loops, recorde NODE IDs in a correctly formatted array
726:     //  We need to uniformly refine the initial geometry to guarantee a valid mesh

728:     // Calculate cell and vertex sizes
729:     PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &nbodies, &bodies, &senses));

731:     PetscCall(PetscHMapICreate(&edgeMap));
732:     PetscCall(PetscHMapICreate(&bodyIndexMap));
733:     PetscCall(PetscHMapICreate(&bodyVertexMap));
734:     PetscCall(PetscHMapICreate(&bodyEdgeMap));
735:     PetscCall(PetscHMapICreate(&bodyEdgeGlobalMap));
736:     PetscCall(PetscHMapICreate(&bodyFaceMap));

738:     for (b = 0; b < nbodies; ++b) {
739:       ego           body = bodies[b];
740:       int           Nf, Ne, Nv;
741:       PetscHashIter BIiter, BViter, BEiter, BEGiter, BFiter, EMiter;
742:       PetscBool     BIfound, BVfound, BEfound, BEGfound, BFfound, EMfound;

744:       PetscCall(PetscHMapIFind(bodyIndexMap, b, &BIiter, &BIfound));
745:       PetscCall(PetscHMapIFind(bodyVertexMap, b, &BViter, &BVfound));
746:       PetscCall(PetscHMapIFind(bodyEdgeMap, b, &BEiter, &BEfound));
747:       PetscCall(PetscHMapIFind(bodyEdgeGlobalMap, b, &BEGiter, &BEGfound));
748:       PetscCall(PetscHMapIFind(bodyFaceMap, b, &BFiter, &BFfound));

750:       if (!BIfound) PetscCall(PetscHMapISet(bodyIndexMap, b, numFaces + numEdges + numVertices));
751:       if (!BVfound) PetscCall(PetscHMapISet(bodyVertexMap, b, numVertices));
752:       if (!BEfound) PetscCall(PetscHMapISet(bodyEdgeMap, b, numEdges));
753:       if (!BEGfound) PetscCall(PetscHMapISet(bodyEdgeGlobalMap, b, edgeCntr));
754:       if (!BFfound) PetscCall(PetscHMapISet(bodyFaceMap, b, numFaces));

756:       PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
757:       PetscCall(EG_getBodyTopos(body, NULL, EDGE, &Ne, &eobjs));
758:       PetscCall(EG_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
759:       EG_free(fobjs);
760:       EG_free(eobjs);
761:       EG_free(nobjs);

763:       // Remove DEGENERATE EDGES from Edge count
764:       PetscCall(EG_getBodyTopos(body, NULL, EDGE, &Ne, &eobjs));
765:       int Netemp = 0;
766:       for (int e = 0; e < Ne; ++e) {
767:         ego edge = eobjs[e];
768:         int eid;

770:         PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
771:         eid = EG_indexBodyTopo(body, edge);

773:         PetscCall(PetscHMapIFind(edgeMap, edgeCntr + eid - 1, &EMiter, &EMfound));
774:         if (mtype == DEGENERATE) {
775:           if (!EMfound) PetscCall(PetscHMapISet(edgeMap, edgeCntr + eid - 1, -1));
776:         } else {
777:           ++Netemp;
778:           if (!EMfound) PetscCall(PetscHMapISet(edgeMap, edgeCntr + eid - 1, Netemp));
779:         }
780:       }
781:       EG_free(eobjs);

783:       // Determine Number of Cells
784:       PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
785:       for (int f = 0; f < Nf; ++f) {
786:         ego face     = fobjs[f];
787:         int edgeTemp = 0;

789:         PetscCall(EG_getBodyTopos(body, face, EDGE, &Ne, &eobjs));
790:         for (int e = 0; e < Ne; ++e) {
791:           ego edge = eobjs[e];

793:           PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
794:           if (mtype != DEGENERATE) ++edgeTemp;
795:         }
796:         numCells += (2 * edgeTemp);
797:         EG_free(eobjs);
798:       }
799:       EG_free(fobjs);

801:       numFaces += Nf;
802:       numEdges += Netemp;
803:       numVertices += Nv;
804:       edgeCntr += Ne;
805:     }

807:     // Set up basic DMPlex parameters
808:     dim        = 2;                                 // Assumes 3D Models :: Need to handle 2D models in the future
809:     cdim       = 3;                                 // Assumes 3D Models :: Need to update to handle 2D models in future
810:     numCorners = 3;                                 // Split Faces into triangles
811:     numPoints  = numVertices + numEdges + numFaces; // total number of coordinate points

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

815:     // Get Vertex Coordinates and Set up Cells
816:     for (b = 0; b < nbodies; ++b) {
817:       ego           body = bodies[b];
818:       int           Nf, Ne, Nv;
819:       PetscInt      bodyVertexIndexStart, bodyEdgeIndexStart, bodyEdgeGlobalIndexStart, bodyFaceIndexStart;
820:       PetscHashIter BViter, BEiter, BEGiter, BFiter, EMiter;
821:       PetscBool     BVfound, BEfound, BEGfound, BFfound, EMfound;

823:       // Vertices on Current Body
824:       PetscCall(EG_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));

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

830:       for (int v = 0; v < Nv; ++v) {
831:         ego    vertex = nobjs[v];
832:         double limits[4];
833:         int    id, dummy;

835:         PetscCall(EG_getTopology(vertex, &geom, &oclass, &mtype, limits, &dummy, &mobjs, &senses));
836:         id = EG_indexBodyTopo(body, vertex);

838:         coords[(bodyVertexIndexStart + id - 1) * cdim + 0] = limits[0];
839:         coords[(bodyVertexIndexStart + id - 1) * cdim + 1] = limits[1];
840:         coords[(bodyVertexIndexStart + id - 1) * cdim + 2] = limits[2];
841:       }
842:       EG_free(nobjs);

844:       // Edge Midpoint Vertices on Current Body
845:       PetscCall(EG_getBodyTopos(body, NULL, EDGE, &Ne, &eobjs));

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

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

855:       for (int e = 0; e < Ne; ++e) {
856:         ego    edge = eobjs[e];
857:         double range[2], avgt[1], cntrPnt[9];
858:         int    eid, eOffset;
859:         int    periodic;

861:         PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
862:         if (mtype == DEGENERATE) continue;

864:         eid = EG_indexBodyTopo(body, edge);

866:         // get relative offset from globalEdgeID Vector
867:         PetscCall(PetscHMapIFind(edgeMap, bodyEdgeGlobalIndexStart + eid - 1, &EMiter, &EMfound));
868:         PetscCheck(EMfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Edge %d not found in edgeMap", bodyEdgeGlobalIndexStart + eid - 1);
869:         PetscCall(PetscHMapIGet(edgeMap, bodyEdgeGlobalIndexStart + eid - 1, &eOffset));

871:         PetscCall(EG_getRange(edge, range, &periodic));
872:         avgt[0] = (range[0] + range[1]) / 2.;

874:         PetscCall(EG_evaluate(edge, avgt, cntrPnt));
875:         coords[(numVertices + bodyEdgeIndexStart + eOffset - 1) * cdim + 0] = cntrPnt[0];
876:         coords[(numVertices + bodyEdgeIndexStart + eOffset - 1) * cdim + 1] = cntrPnt[1];
877:         coords[(numVertices + bodyEdgeIndexStart + eOffset - 1) * cdim + 2] = cntrPnt[2];
878:       }
879:       EG_free(eobjs);

881:       // Face Midpoint Vertices on Current Body
882:       PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));

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

888:       for (int f = 0; f < Nf; ++f) {
889:         ego    face = fobjs[f];
890:         double range[4], avgUV[2], cntrPnt[18];
891:         int    peri, id;

893:         id = EG_indexBodyTopo(body, face);
894:         PetscCall(EG_getRange(face, range, &peri));

896:         avgUV[0] = (range[0] + range[1]) / 2.;
897:         avgUV[1] = (range[2] + range[3]) / 2.;
898:         PetscCall(EG_evaluate(face, avgUV, cntrPnt));

900:         coords[(numVertices + numEdges + bodyFaceIndexStart + id - 1) * cdim + 0] = cntrPnt[0];
901:         coords[(numVertices + numEdges + bodyFaceIndexStart + id - 1) * cdim + 1] = cntrPnt[1];
902:         coords[(numVertices + numEdges + bodyFaceIndexStart + id - 1) * cdim + 2] = cntrPnt[2];
903:       }
904:       EG_free(fobjs);

906:       // Define Cells :: Note - This could be incorporated in the Face Midpoint Vertices Loop but was kept separate for clarity
907:       PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
908:       for (int f = 0; f < Nf; ++f) {
909:         ego face = fobjs[f];
910:         int fID, midFaceID, midPntID, startID, endID, Nl;

912:         fID       = EG_indexBodyTopo(body, face);
913:         midFaceID = numVertices + numEdges + bodyFaceIndexStart + fID - 1;
914:         // Must Traverse Loop to ensure we have all necessary information like the sense (+/- 1) of the edges.
915:         // TODO :: Only handles single loop faces (No holes). The choices for handling multiloop faces are:
916:         //            1) Use the DMPlexCreateEGADSFromFile() with the -dm_plex_egads_with_tess = 1 option.
917:         //               This will use a default EGADS tessellation as an initial surface mesh.
918:         //            2) Create the initial surface mesh via a 2D mesher :: Currently not available (?future?)
919:         //               May I suggest the XXXX as a starting point?

921:         PetscCall(EG_getTopology(face, &geom, &oclass, &mtype, NULL, &Nl, &lobjs, &lSenses));

923:         PetscCheck(Nl <= 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Face has %d Loops. Can only handle Faces with 1 Loop. Please use --dm_plex_egads_with_tess = 1 Option", Nl);
924:         for (int l = 0; l < Nl; ++l) {
925:           ego loop = lobjs[l];

927:           PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &eobjs, &eSenses));
928:           for (int e = 0; e < Ne; ++e) {
929:             ego edge = eobjs[e];
930:             int eid, eOffset;

932:             PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
933:             eid = EG_indexBodyTopo(body, edge);
934:             if (mtype == DEGENERATE) continue;

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

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

943:             PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));

945:             if (eSenses[e] > 0) {
946:               startID = EG_indexBodyTopo(body, nobjs[0]);
947:               endID   = EG_indexBodyTopo(body, nobjs[1]);
948:             } else {
949:               startID = EG_indexBodyTopo(body, nobjs[1]);
950:               endID   = EG_indexBodyTopo(body, nobjs[0]);
951:             }

953:             // Define 2 Cells per Edge with correct orientation
954:             cells[cellCntr * numCorners + 0] = midFaceID;
955:             cells[cellCntr * numCorners + 1] = bodyVertexIndexStart + startID - 1;
956:             cells[cellCntr * numCorners + 2] = midPntID;

958:             cells[cellCntr * numCorners + 3] = midFaceID;
959:             cells[cellCntr * numCorners + 4] = midPntID;
960:             cells[cellCntr * numCorners + 5] = bodyVertexIndexStart + endID - 1;

962:             cellCntr = cellCntr + 2;
963:           }
964:         }
965:       }
966:       EG_free(fobjs);
967:     }
968:   }

970:   // Generate DMPlex
971:   PetscCall(DMPlexCreateFromCellListPetsc(PETSC_COMM_WORLD, dim, numCells, numPoints, numCorners, PETSC_TRUE, cells, cdim, coords, &dm));
972:   PetscCall(PetscFree2(coords, cells));
973:   PetscCall(PetscInfo(dm, " Total Number of Unique Cells    = %" PetscInt_FMT " \n", numCells));
974:   PetscCall(PetscInfo(dm, " Total Number of Unique Vertices = %" PetscInt_FMT " \n", numVertices));

976:   // Embed EGADS model in DM
977:   {
978:     PetscContainer modelObj, contextObj;

980:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &modelObj));
981:     PetscCall(PetscContainerSetPointer(modelObj, model));
982:     PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Model", (PetscObject)modelObj));
983:     PetscCall(PetscContainerDestroy(&modelObj));

985:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &contextObj));
986:     PetscCall(PetscContainerSetPointer(contextObj, context));
987:     PetscCall(PetscContainerSetCtxDestroy(contextObj, DMPlexEGADSDestroy_Private));
988:     PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Context", (PetscObject)contextObj));
989:     PetscCall(PetscContainerDestroy(&contextObj));
990:   }
991:   // Label points
992:   PetscInt nStart, nEnd;

994:   PetscCall(DMCreateLabel(dm, "EGADS Body ID"));
995:   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
996:   PetscCall(DMCreateLabel(dm, "EGADS Face ID"));
997:   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
998:   PetscCall(DMCreateLabel(dm, "EGADS Edge ID"));
999:   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
1000:   PetscCall(DMCreateLabel(dm, "EGADS Vertex ID"));
1001:   PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));

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

1005:   cellCntr = 0;
1006:   for (b = 0; b < nbodies; ++b) {
1007:     ego           body = bodies[b];
1008:     int           Nv, Ne, Nf;
1009:     PetscInt      bodyVertexIndexStart, bodyEdgeIndexStart, bodyEdgeGlobalIndexStart, bodyFaceIndexStart;
1010:     PetscHashIter BViter, BEiter, BEGiter, BFiter, EMiter;
1011:     PetscBool     BVfound, BEfound, BEGfound, BFfound, EMfound;

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

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

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

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

1029:     PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
1030:     for (int f = 0; f < Nf; ++f) {
1031:       ego face = fobjs[f];
1032:       int fID, Nl;

1034:       fID = EG_indexBodyTopo(body, face);

1036:       PetscCall(EG_getBodyTopos(body, face, LOOP, &Nl, &lobjs));
1037:       for (int l = 0; l < Nl; ++l) {
1038:         ego loop = lobjs[l];
1039:         int lid;

1041:         lid = EG_indexBodyTopo(body, loop);
1042:         PetscCheck(Nl <= 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Loop %d has %d > 1 faces, which is not supported", lid, Nf);

1044:         PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &eobjs, &eSenses));
1045:         for (int e = 0; e < Ne; ++e) {
1046:           ego edge = eobjs[e];
1047:           int eid, eOffset;

1049:           // Skip DEGENERATE Edges
1050:           PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
1051:           if (mtype == DEGENERATE) continue;
1052:           eid = EG_indexBodyTopo(body, edge);

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

1059:           PetscCall(EG_getBodyTopos(body, edge, NODE, &Nv, &nobjs));
1060:           for (int v = 0; v < Nv; ++v) {
1061:             ego vertex = nobjs[v];
1062:             int vID;

1064:             vID = EG_indexBodyTopo(body, vertex);
1065:             PetscCall(DMLabelSetValue(bodyLabel, nStart + bodyVertexIndexStart + vID - 1, b));
1066:             PetscCall(DMLabelSetValue(vertexLabel, nStart + bodyVertexIndexStart + vID - 1, vID));
1067:           }
1068:           EG_free(nobjs);

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

1073:           // Define Cell faces
1074:           for (int jj = 0; jj < 2; ++jj) {
1075:             PetscCall(DMLabelSetValue(bodyLabel, cellCntr, b));
1076:             PetscCall(DMLabelSetValue(faceLabel, cellCntr, fID));
1077:             PetscCall(DMPlexGetCone(dm, cellCntr, &cone));

1079:             PetscCall(DMLabelSetValue(bodyLabel, cone[0], b));
1080:             PetscCall(DMLabelSetValue(faceLabel, cone[0], fID));

1082:             PetscCall(DMLabelSetValue(bodyLabel, cone[1], b));
1083:             PetscCall(DMLabelSetValue(edgeLabel, cone[1], eid));

1085:             PetscCall(DMLabelSetValue(bodyLabel, cone[2], b));
1086:             PetscCall(DMLabelSetValue(faceLabel, cone[2], fID));

1088:             cellCntr = cellCntr + 1;
1089:           }
1090:         }
1091:       }
1092:       EG_free(lobjs);

1094:       PetscCall(DMLabelSetValue(bodyLabel, nStart + numVertices + numEdges + bodyFaceIndexStart + fID - 1, b));
1095:       PetscCall(DMLabelSetValue(faceLabel, nStart + numVertices + numEdges + bodyFaceIndexStart + fID - 1, fID));
1096:     }
1097:     EG_free(fobjs);
1098:   }

1100:   PetscCall(PetscHMapIDestroy(&edgeMap));
1101:   PetscCall(PetscHMapIDestroy(&bodyIndexMap));
1102:   PetscCall(PetscHMapIDestroy(&bodyVertexMap));
1103:   PetscCall(PetscHMapIDestroy(&bodyEdgeMap));
1104:   PetscCall(PetscHMapIDestroy(&bodyEdgeGlobalMap));
1105:   PetscCall(PetscHMapIDestroy(&bodyFaceMap));

1107:   *newdm = dm;
1108:   PetscFunctionReturn(PETSC_SUCCESS);
1109: }

1111: static PetscErrorCode DMPlexCreateEGADS_Tess_Internal(MPI_Comm comm, ego context, ego model, DM *newdm)
1112: {
1113:   DMLabel bodyLabel, faceLabel, edgeLabel, vertexLabel;
1114:   /* EGADSLite variables */
1115:   ego    geom, *bodies, *fobjs;
1116:   int    b, oclass, mtype, nbodies, *senses;
1117:   int    totalNumTris = 0, totalNumPoints = 0;
1118:   double boundBox[6] = {0., 0., 0., 0., 0., 0.}, tessSize;
1119:   /* PETSc variables */
1120:   DM              dm;
1121:   PetscHMapI      pointIndexStartMap = NULL, triIndexStartMap = NULL, pTypeLabelMap = NULL, pIndexLabelMap = NULL;
1122:   PetscHMapI      pBodyIndexLabelMap = NULL, triFaceIDLabelMap = NULL, triBodyIDLabelMap = NULL;
1123:   PetscInt        dim = -1, cdim = -1, numCorners = 0, counter = 0;
1124:   PetscInt       *cells  = NULL;
1125:   const PetscInt *cone   = NULL;
1126:   PetscReal      *coords = NULL;
1127:   PetscMPIInt     rank;

1129:   PetscFunctionBeginUser;
1130:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
1131:   if (rank == 0) {
1132:     // ---------------------------------------------------------------------------------------------------
1133:     // Generate Petsc Plex from EGADSlite created Tessellation of geometry
1134:     // ---------------------------------------------------------------------------------------------------

1136:     // Calculate cell and vertex sizes
1137:     PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &nbodies, &bodies, &senses));

1139:     PetscCall(PetscHMapICreate(&pointIndexStartMap));
1140:     PetscCall(PetscHMapICreate(&triIndexStartMap));
1141:     PetscCall(PetscHMapICreate(&pTypeLabelMap));
1142:     PetscCall(PetscHMapICreate(&pIndexLabelMap));
1143:     PetscCall(PetscHMapICreate(&pBodyIndexLabelMap));
1144:     PetscCall(PetscHMapICreate(&triFaceIDLabelMap));
1145:     PetscCall(PetscHMapICreate(&triBodyIDLabelMap));

1147:     /* Create Tessellation of Bodies */
1148:     ego tessArray[nbodies];

1150:     for (b = 0; b < nbodies; ++b) {
1151:       ego           body      = bodies[b];
1152:       double        params[3] = {0.0, 0.0, 0.0}; // Parameters for Tessellation
1153:       int           Nf, bodyNumPoints = 0, bodyNumTris = 0;
1154:       PetscHashIter PISiter, TISiter;
1155:       PetscBool     PISfound, TISfound;

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

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

1164:       /* Calculate Tessellation parameters based on Bounding Box */
1165:       /* Get Bounding Box Dimensions of the BODY */
1166:       PetscCall(EG_getBoundingBox(body, boundBox));
1167:       tessSize = boundBox[3] - boundBox[0];
1168:       if (tessSize < boundBox[4] - boundBox[1]) tessSize = boundBox[4] - boundBox[1];
1169:       if (tessSize < boundBox[5] - boundBox[2]) tessSize = boundBox[5] - boundBox[2];

1171:       // TODO :: May want to give users tessellation parameter options //
1172:       params[0] = 0.0250 * tessSize;
1173:       params[1] = 0.0075 * tessSize;
1174:       params[2] = 15.0;

1176:       PetscCall(EG_makeTessBody(body, params, &tessArray[b]));

1178:       PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));

1180:       for (int f = 0; f < Nf; ++f) {
1181:         ego           face = fobjs[f];
1182:         int           len, fID, ntris;
1183:         const int    *ptype, *pindex, *ptris, *ptric;
1184:         const double *pxyz, *puv;

1186:         // Get Face ID //
1187:         fID = EG_indexBodyTopo(body, face);

1189:         // Checkout the Surface Tessellation //
1190:         PetscCall(EG_getTessFace(tessArray[b], fID, &len, &pxyz, &puv, &ptype, &pindex, &ntris, &ptris, &ptric));

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

1195:         // Check out the point index and coordinate //
1196:         for (int p = 0; p < len; ++p) {
1197:           int global;

1199:           PetscCall(EG_localToGlobal(tessArray[b], fID, p + 1, &global));

1201:           // Determine the total number of points in the tessellation //
1202:           bodyNumPoints = PetscMax(bodyNumPoints, global);
1203:         }
1204:       }
1205:       EG_free(fobjs);

1207:       totalNumPoints += bodyNumPoints;
1208:       totalNumTris += bodyNumTris;
1209:     }
1210:     //}  - Original End of (rank == 0)

1212:     dim        = 2;
1213:     cdim       = 3;
1214:     numCorners = 3;
1215:     //PetscInt counter = 0;

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

1221:     for (b = 0; b < nbodies; ++b) {
1222:       ego           body = bodies[b];
1223:       int           Nf;
1224:       PetscInt      pointIndexStart;
1225:       PetscHashIter PISiter;
1226:       PetscBool     PISfound;

1228:       PetscCall(PetscHMapIFind(pointIndexStartMap, b, &PISiter, &PISfound));
1229:       PetscCheck(PISfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in pointIndexStartMap", b);
1230:       PetscCall(PetscHMapIGet(pointIndexStartMap, b, &pointIndexStart));

1232:       PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));

1234:       for (int f = 0; f < Nf; ++f) {
1235:         /* Get Face Object */
1236:         ego           face = fobjs[f];
1237:         int           len, fID, ntris;
1238:         const int    *ptype, *pindex, *ptris, *ptric;
1239:         const double *pxyz, *puv;

1241:         /* Get Face ID */
1242:         fID = EG_indexBodyTopo(body, face);

1244:         /* Checkout the Surface Tessellation */
1245:         PetscCall(EG_getTessFace(tessArray[b], fID, &len, &pxyz, &puv, &ptype, &pindex, &ntris, &ptris, &ptric));

1247:         /* Check out the point index and coordinate */
1248:         for (int p = 0; p < len; ++p) {
1249:           int           global;
1250:           PetscHashIter PTLiter, PILiter, PBLiter;
1251:           PetscBool     PTLfound, PILfound, PBLfound;

1253:           PetscCall(EG_localToGlobal(tessArray[b], fID, p + 1, &global));

1255:           /* Set the coordinates array for DAG */
1256:           coords[((global - 1 + pointIndexStart) * 3) + 0] = pxyz[(p * 3) + 0];
1257:           coords[((global - 1 + pointIndexStart) * 3) + 1] = pxyz[(p * 3) + 1];
1258:           coords[((global - 1 + pointIndexStart) * 3) + 2] = pxyz[(p * 3) + 2];

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

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

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

1272:         for (int t = 0; t < (int)ntris; ++t) {
1273:           int           global, globalA, globalB;
1274:           PetscHashIter TFLiter, TBLiter;
1275:           PetscBool     TFLfound, TBLfound;

1277:           PetscCall(EG_localToGlobal(tessArray[b], fID, ptris[(t * 3) + 0], &global));
1278:           cells[(counter * 3) + 0] = global - 1 + pointIndexStart;

1280:           PetscCall(EG_localToGlobal(tessArray[b], fID, ptris[(t * 3) + 1], &globalA));
1281:           cells[(counter * 3) + 1] = globalA - 1 + pointIndexStart;

1283:           PetscCall(EG_localToGlobal(tessArray[b], fID, ptris[(t * 3) + 2], &globalB));
1284:           cells[(counter * 3) + 2] = globalB - 1 + pointIndexStart;

1286:           PetscCall(PetscHMapIFind(triFaceIDLabelMap, counter, &TFLiter, &TFLfound));
1287:           PetscCall(PetscHMapIFind(triBodyIDLabelMap, counter, &TBLiter, &TBLfound));

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

1292:           counter += 1;
1293:         }
1294:       }
1295:       EG_free(fobjs);
1296:     }
1297:   }

1299:   //Build DMPlex
1300:   PetscCall(DMPlexCreateFromCellListPetsc(PETSC_COMM_WORLD, dim, totalNumTris, totalNumPoints, numCorners, PETSC_TRUE, cells, cdim, coords, &dm));
1301:   PetscCall(PetscFree2(coords, cells));

1303:   // Embed EGADS model in DM
1304:   {
1305:     PetscContainer modelObj, contextObj;

1307:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &modelObj));
1308:     PetscCall(PetscContainerSetPointer(modelObj, model));
1309:     PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Model", (PetscObject)modelObj));
1310:     PetscCall(PetscContainerDestroy(&modelObj));

1312:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &contextObj));
1313:     PetscCall(PetscContainerSetPointer(contextObj, context));
1314:     PetscCall(PetscContainerSetCtxDestroy(contextObj, DMPlexEGADSDestroy_Private));
1315:     PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Context", (PetscObject)contextObj));
1316:     PetscCall(PetscContainerDestroy(&contextObj));
1317:   }

1319:   // Label Points
1320:   PetscCall(DMCreateLabel(dm, "EGADS Body ID"));
1321:   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
1322:   PetscCall(DMCreateLabel(dm, "EGADS Face ID"));
1323:   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
1324:   PetscCall(DMCreateLabel(dm, "EGADS Edge ID"));
1325:   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
1326:   PetscCall(DMCreateLabel(dm, "EGADS Vertex ID"));
1327:   PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));

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

1332:   PetscCall(DMPlexGetHeightStratum(dm, 0, &fStart, &fEnd));
1333:   PetscCall(DMPlexGetHeightStratum(dm, 1, &eStart, &eEnd));
1334:   PetscCall(DMPlexGetHeightStratum(dm, 2, &nStart, &nEnd));

1336:   /* Set DMLabels for NODES */
1337:   for (int n = nStart; n < nEnd; ++n) {
1338:     int           pTypeVal, pIndexVal, pBodyVal;
1339:     PetscHashIter PTLiter, PILiter, PBLiter;
1340:     PetscBool     PTLfound, PILfound, PBLfound;

1342:     //Converted to Hash Tables
1343:     PetscCall(PetscHMapIFind(pTypeLabelMap, n - nStart, &PTLiter, &PTLfound));
1344:     PetscCheck(PTLfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "DAG Point %d not found in pTypeLabelMap", n);
1345:     PetscCall(PetscHMapIGet(pTypeLabelMap, n - nStart, &pTypeVal));

1347:     PetscCall(PetscHMapIFind(pIndexLabelMap, n - nStart, &PILiter, &PILfound));
1348:     PetscCheck(PILfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "DAG Point %d not found in pIndexLabelMap", n);
1349:     PetscCall(PetscHMapIGet(pIndexLabelMap, n - nStart, &pIndexVal));

1351:     PetscCall(PetscHMapIFind(pBodyIndexLabelMap, n - nStart, &PBLiter, &PBLfound));
1352:     PetscCheck(PBLfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "DAG Point %d not found in pBodyLabelMap", n);
1353:     PetscCall(PetscHMapIGet(pBodyIndexLabelMap, n - nStart, &pBodyVal));

1355:     PetscCall(DMLabelSetValue(bodyLabel, n, pBodyVal));
1356:     if (pTypeVal == 0) PetscCall(DMLabelSetValue(vertexLabel, n, pIndexVal));
1357:     if (pTypeVal > 0) PetscCall(DMLabelSetValue(edgeLabel, n, pIndexVal));
1358:     if (pTypeVal < 0) PetscCall(DMLabelSetValue(faceLabel, n, pIndexVal));
1359:   }

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

1365:     PetscCall(DMPlexGetCone(dm, e, &cone));
1366:     PetscCall(DMLabelGetValue(bodyLabel, cone[0], &bodyID_0)); // Do I need to check the other end?
1367:     PetscCall(DMLabelGetValue(vertexLabel, cone[0], &vertexID_0));
1368:     PetscCall(DMLabelGetValue(vertexLabel, cone[1], &vertexID_1));
1369:     PetscCall(DMLabelGetValue(edgeLabel, cone[0], &edgeID_0));
1370:     PetscCall(DMLabelGetValue(edgeLabel, cone[1], &edgeID_1));
1371:     PetscCall(DMLabelGetValue(faceLabel, cone[0], &faceID_0));
1372:     PetscCall(DMLabelGetValue(faceLabel, cone[1], &faceID_1));

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

1376:     if (edgeID_0 == edgeID_1) PetscCall(DMLabelSetValue(edgeLabel, e, edgeID_0));
1377:     else if (vertexID_0 > 0 && edgeID_1 > 0) PetscCall(DMLabelSetValue(edgeLabel, e, edgeID_1));
1378:     else if (vertexID_1 > 0 && edgeID_0 > 0) PetscCall(DMLabelSetValue(edgeLabel, e, edgeID_0));
1379:     else { /* Do Nothing */ }
1380:   }

1382:   /* Set DMLabels for Cells */
1383:   for (int f = fStart; f < fEnd; ++f) {
1384:     int           edgeID_0;
1385:     PetscInt      triBodyVal, triFaceVal;
1386:     PetscHashIter TFLiter, TBLiter;
1387:     PetscBool     TFLfound, TBLfound;

1389:     // Convert to Hash Table
1390:     PetscCall(PetscHMapIFind(triFaceIDLabelMap, f - fStart, &TFLiter, &TFLfound));
1391:     PetscCheck(TFLfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "DAG Point %d not found in triFaceIDLabelMap", f);
1392:     PetscCall(PetscHMapIGet(triFaceIDLabelMap, f - fStart, &triFaceVal));

1394:     PetscCall(PetscHMapIFind(triBodyIDLabelMap, f - fStart, &TBLiter, &TBLfound));
1395:     PetscCheck(TBLfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "DAG Point %d not found in triBodyIDLabelMap", f);
1396:     PetscCall(PetscHMapIGet(triBodyIDLabelMap, f - fStart, &triBodyVal));

1398:     PetscCall(DMLabelSetValue(bodyLabel, f, triBodyVal));
1399:     PetscCall(DMLabelSetValue(faceLabel, f, triFaceVal));

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

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

1407:       if (edgeID_0 < 0) {
1408:         PetscCall(DMLabelSetValue(bodyLabel, cone[jj], triBodyVal));
1409:         PetscCall(DMLabelSetValue(faceLabel, cone[jj], triFaceVal));
1410:       }
1411:     }
1412:   }

1414:   *newdm = dm;
1415:   PetscFunctionReturn(PETSC_SUCCESS);
1416: }
1417: #endif

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

1423:   Collective

1425:   Input Parameter:
1426: . dm - The uninflated `DM` object representing the mesh

1428:   Level: intermediate

1430: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexCreateEGADS()`
1431: @*/
1432: PetscErrorCode DMPlexInflateToGeomModel(DM dm)
1433: {
1434: #if defined(PETSC_HAVE_EGADS)
1435:   /* EGADS Variables */
1436:   ego    model, geom, body, face, edge;
1437:   ego   *bodies;
1438:   int    Nb, oclass, mtype, *senses;
1439:   double result[3];
1440:   /* PETSc Variables */
1441:   DM             cdm;
1442:   PetscContainer modelObj;
1443:   DMLabel        bodyLabel, faceLabel, edgeLabel, vertexLabel;
1444:   Vec            coordinates;
1445:   PetscScalar   *coords;
1446:   PetscInt       bodyID, faceID, edgeID, vertexID;
1447:   PetscInt       cdim, d, vStart, vEnd, v;
1448: #endif

1450:   PetscFunctionBegin;
1451: #if defined(PETSC_HAVE_EGADS)
1452:   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
1453:   if (!modelObj) PetscFunctionReturn(PETSC_SUCCESS);
1454:   PetscCall(DMGetCoordinateDim(dm, &cdim));
1455:   PetscCall(DMGetCoordinateDM(dm, &cdm));
1456:   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1457:   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
1458:   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
1459:   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
1460:   PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));

1462:   PetscCall(PetscContainerGetPointer(modelObj, (void **)&model));
1463:   PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));

1465:   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1466:   PetscCall(VecGetArrayWrite(coordinates, &coords));
1467:   for (v = vStart; v < vEnd; ++v) {
1468:     PetscScalar *vcoords;

1470:     PetscCall(DMLabelGetValue(bodyLabel, v, &bodyID));
1471:     PetscCall(DMLabelGetValue(faceLabel, v, &faceID));
1472:     PetscCall(DMLabelGetValue(edgeLabel, v, &edgeID));
1473:     PetscCall(DMLabelGetValue(vertexLabel, v, &vertexID));

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

1478:     PetscCall(DMPlexPointLocalRef(cdm, v, coords, (void *)&vcoords));
1479:     if (edgeID > 0) {
1480:       /* Snap to EDGE at nearest location */
1481:       double params[1];
1482:       PetscCall(EG_objectBodyTopo(body, EDGE, edgeID, &edge));
1483:       PetscCall(EG_invEvaluate(edge, vcoords, params, result)); // Get (x,y,z) of nearest point on EDGE
1484:       for (d = 0; d < cdim; ++d) vcoords[d] = result[d];
1485:     } else if (faceID > 0) {
1486:       /* Snap to FACE at nearest location */
1487:       double params[2];
1488:       PetscCall(EG_objectBodyTopo(body, FACE, faceID, &face));
1489:       PetscCall(EG_invEvaluate(face, vcoords, params, result)); // Get (x,y,z) of nearest point on FACE
1490:       for (d = 0; d < cdim; ++d) vcoords[d] = result[d];
1491:     }
1492:   }
1493:   PetscCall(VecRestoreArrayWrite(coordinates, &coords));
1494:   /* Clear out global coordinates */
1495:   PetscCall(VecDestroy(&dm->coordinates[0].x));
1496: #endif
1497:   PetscFunctionReturn(PETSC_SUCCESS);
1498: }

1500: /*@
1501:   DMPlexCreateEGADSFromFile - Create a `DMPLEX` mesh from an EGADS, IGES, or STEP file.

1503:   Collective

1505:   Input Parameters:
1506: + comm     - The MPI communicator
1507: - filename - The name of the EGADS, IGES, or STEP file

1509:   Output Parameter:
1510: . dm - The `DM` object representing the mesh

1512:   Level: beginner

1514: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexCreateEGADS()`, `DMPlexCreateEGADSLiteFromFile()`
1515: @*/
1516: PetscErrorCode DMPlexCreateEGADSFromFile(MPI_Comm comm, const char filename[], DM *dm)
1517: {
1518:   PetscMPIInt rank;
1519: #if defined(PETSC_HAVE_EGADS)
1520:   ego context = NULL, model = NULL;
1521: #endif
1522:   PetscBool printModel = PETSC_FALSE, tessModel = PETSC_FALSE, newModel = PETSC_FALSE;

1524:   PetscFunctionBegin;
1525:   PetscAssertPointer(filename, 2);
1526:   PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_egads_print_model", &printModel, NULL));
1527:   PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_egads_tess_model", &tessModel, NULL));
1528:   PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_egads_new_model", &newModel, NULL));
1529:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
1530: #if defined(PETSC_HAVE_EGADS)
1531:   if (rank == 0) {
1532:     PetscCall(EG_open(&context));
1533:     PetscCall(EG_loadModel(context, 0, filename, &model));
1534:     if (printModel) PetscCall(DMPlexEGADSPrintModel_Internal(model));
1535:   }
1536:   if (tessModel) PetscCall(DMPlexCreateEGADS_Tess_Internal(comm, context, model, dm));
1537:   else if (newModel) PetscCall(DMPlexCreateEGADS(comm, context, model, dm));
1538:   else PetscCall(DMPlexCreateEGADS_Internal(comm, context, model, dm));
1539:   PetscFunctionReturn(PETSC_SUCCESS);
1540: #else
1541:   SETERRQ(comm, PETSC_ERR_SUP, "This method requires EGADS support. Reconfigure using --download-egads");
1542: #endif
1543: }