xref: /petsc/src/dm/impls/plex/plexegads.c (revision 0619917b5a674bb687c64e7daba2ab22be99af31)
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/hashmapi.h>
3 
4 #ifdef PETSC_HAVE_EGADS
5   #include <egads.h>
6 #endif
7 
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:
10 
11      https://github.com/tpaviot/oce/tree/master/src/STEPControl
12 
13    The STEP, and inner EXPRESS, formats are ISO standards, so they are documented
14 
15      https://stackoverflow.com/questions/26774037/documentation-or-specification-for-step-and-stp-files
16      http://stepmod.sourceforge.net/express_model_spec/
17 
18    but again it seems that there has been a deliberate effort at obfuscation, probably to raise the bar for entrants.
19 */
20 
21 #ifdef PETSC_HAVE_EGADS
22 PETSC_INTERN PetscErrorCode DMPlexSnapToGeomModel_EGADS_Internal(DM, PetscInt, ego, PetscInt, PetscInt, PetscInt, const PetscScalar[], PetscScalar[]);
23 PETSC_INTERN PetscErrorCode DMPlexSnapToGeomModel_EGADSLite_Internal(DM, PetscInt, ego, PetscInt, PetscInt, PetscInt, const PetscScalar[], PetscScalar[]);
24 
25 PetscErrorCode DMPlexSnapToGeomModel_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;
37 
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];
45 
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   }
56 
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);
66 
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
103 
104 /*@
105   DMPlexSnapToGeomModel - Given a coordinate point 'mcoords' on the mesh point 'p', return the closest coordinate point 'gcoords' on the geometry model associated with that point.
106 
107   Not Collective
108 
109   Input Parameters:
110 + dm      - The `DMPLEX` object
111 . p       - The mesh point
112 . dE      - The coordinate dimension
113 - mcoords - A coordinate point lying on the mesh point
114 
115   Output Parameter:
116 . gcoords - The closest coordinate point on the geometry model associated with 'p' to the given point
117 
118   Level: intermediate
119 
120   Note:
121   Returns the original coordinates if no geometry model is found. Right now the only supported geometry model is EGADS.
122 
123   The coordinate dimension may be different from the coordinate dimension of the `dm`, for example if the transformation is extrusion.
124 
125 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMRefine()`, `DMPlexCreate()`, `DMPlexSetRefinementUniform()`
126 @*/
127 PetscErrorCode DMPlexSnapToGeomModel(DM dm, PetscInt p, PetscInt dE, const PetscScalar mcoords[], PetscScalar gcoords[])
128 {
129   PetscInt d;
130 
131   PetscFunctionBeginHot;
132 #ifdef PETSC_HAVE_EGADS
133   {
134     DM_Plex       *plex = (DM_Plex *)dm->data;
135     DMLabel        bodyLabel, faceLabel, edgeLabel;
136     PetscInt       bodyID, faceID, edgeID;
137     PetscContainer modelObj;
138     ego            model;
139     PetscBool      islite = PETSC_FALSE;
140 
141     PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
142     PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
143     PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
144     if (!bodyLabel || !faceLabel || !edgeLabel || plex->ignoreModel) {
145       for (d = 0; d < dE; ++d) gcoords[d] = mcoords[d];
146       PetscFunctionReturn(PETSC_SUCCESS);
147     }
148     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
149     if (!modelObj) {
150       PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSLite Model", (PetscObject *)&modelObj));
151       islite = PETSC_TRUE;
152     }
153     if (!modelObj) {
154       for (d = 0; d < dE; ++d) gcoords[d] = mcoords[d];
155       PetscFunctionReturn(PETSC_SUCCESS);
156     }
157     PetscCall(PetscContainerGetPointer(modelObj, (void **)&model));
158     PetscCall(DMLabelGetValue(bodyLabel, p, &bodyID));
159     PetscCall(DMLabelGetValue(faceLabel, p, &faceID));
160     PetscCall(DMLabelGetValue(edgeLabel, p, &edgeID));
161     /* Allows for "Connective" Plex Edges present in models with multiple non-touching Entities */
162     if (bodyID < 0) {
163       for (d = 0; d < dE; ++d) gcoords[d] = mcoords[d];
164       PetscFunctionReturn(PETSC_SUCCESS);
165     }
166     if (islite) PetscCall(DMPlexSnapToGeomModel_EGADSLite_Internal(dm, p, model, bodyID, faceID, edgeID, mcoords, gcoords));
167     else PetscCall(DMPlexSnapToGeomModel_EGADS_Internal(dm, p, model, bodyID, faceID, edgeID, mcoords, gcoords));
168   }
169 #else
170   for (d = 0; d < dE; ++d) gcoords[d] = mcoords[d];
171 #endif
172   PetscFunctionReturn(PETSC_SUCCESS);
173 }
174 
175 #if defined(PETSC_HAVE_EGADS)
176 static PetscErrorCode DMPlexEGADSPrintModel_Internal(ego model)
177 {
178   ego geom, *bodies, *objs, *nobjs, *mobjs, *lobjs;
179   int oclass, mtype, *senses;
180   int Nb, b;
181 
182   PetscFunctionBeginUser;
183   /* test bodyTopo functions */
184   PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
185   PetscCall(PetscPrintf(PETSC_COMM_SELF, " Number of BODIES (nbodies): %d \n", Nb));
186 
187   for (b = 0; b < Nb; ++b) {
188     ego body = bodies[b];
189     int id, Nsh, Nf, Nl, l, Ne, e, Nv, v;
190 
191     /* Output Basic Model Topology */
192     PetscCall(EG_getBodyTopos(body, NULL, SHELL, &Nsh, &objs));
193     PetscCall(PetscPrintf(PETSC_COMM_SELF, "   Number of SHELLS: %d \n", Nsh));
194     EG_free(objs);
195 
196     PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &objs));
197     PetscCall(PetscPrintf(PETSC_COMM_SELF, "   Number of FACES: %d \n", Nf));
198     EG_free(objs);
199 
200     PetscCall(EG_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
201     PetscCall(PetscPrintf(PETSC_COMM_SELF, "   Number of LOOPS: %d \n", Nl));
202 
203     PetscCall(EG_getBodyTopos(body, NULL, EDGE, &Ne, &objs));
204     PetscCall(PetscPrintf(PETSC_COMM_SELF, "   Number of EDGES: %d \n", Ne));
205     EG_free(objs);
206 
207     PetscCall(EG_getBodyTopos(body, NULL, NODE, &Nv, &objs));
208     PetscCall(PetscPrintf(PETSC_COMM_SELF, "   Number of NODES: %d \n", Nv));
209     EG_free(objs);
210 
211     for (l = 0; l < Nl; ++l) {
212       ego loop = lobjs[l];
213 
214       id = EG_indexBodyTopo(body, loop);
215       PetscCall(PetscPrintf(PETSC_COMM_SELF, "          LOOP ID: %d\n", id));
216 
217       /* Get EDGE info which associated with the current LOOP */
218       PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));
219 
220       for (e = 0; e < Ne; ++e) {
221         ego    edge      = objs[e];
222         double range[4]  = {0., 0., 0., 0.};
223         double point[3]  = {0., 0., 0.};
224         double params[3] = {0., 0., 0.};
225         double result[18];
226         int    peri;
227 
228         id = EG_indexBodyTopo(body, edge);
229         PetscCall(PetscPrintf(PETSC_COMM_SELF, "            EDGE ID: %d (%d)\n", id, e));
230 
231         PetscCall(EG_getRange(edge, range, &peri));
232         PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Range = %lf, %lf, %lf, %lf \n", range[0], range[1], range[2], range[3]));
233 
234         /* Get NODE info which associated with the current EDGE */
235         PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
236         if (mtype == DEGENERATE) {
237           PetscCall(PetscPrintf(PETSC_COMM_SELF, "  EDGE %d is DEGENERATE \n", id));
238         } else {
239           params[0] = range[0];
240           PetscCall(EG_evaluate(edge, params, result));
241           PetscCall(PetscPrintf(PETSC_COMM_SELF, "   between (%lf, %lf, %lf)", result[0], result[1], result[2]));
242           params[0] = range[1];
243           PetscCall(EG_evaluate(edge, params, result));
244           PetscCall(PetscPrintf(PETSC_COMM_SELF, " and (%lf, %lf, %lf)\n", result[0], result[1], result[2]));
245         }
246 
247         for (v = 0; v < Nv; ++v) {
248           ego    vertex = nobjs[v];
249           double limits[4];
250           int    dummy;
251 
252           PetscCall(EG_getTopology(vertex, &geom, &oclass, &mtype, limits, &dummy, &mobjs, &senses));
253           id = EG_indexBodyTopo(body, vertex);
254           PetscCall(PetscPrintf(PETSC_COMM_SELF, "              NODE ID: %d \n", id));
255           PetscCall(PetscPrintf(PETSC_COMM_SELF, "                 (x, y, z) = (%lf, %lf, %lf) \n", limits[0], limits[1], limits[2]));
256 
257           point[0] = point[0] + limits[0];
258           point[1] = point[1] + limits[1];
259           point[2] = point[2] + limits[2];
260         }
261       }
262     }
263     EG_free(lobjs);
264   }
265   PetscFunctionReturn(PETSC_SUCCESS);
266 }
267 
268 static PetscErrorCode DMPlexEGADSDestroy_Private(void *context)
269 {
270   if (context) EG_close((ego)context);
271   return 0;
272 }
273 
274 static PetscErrorCode DMPlexCreateEGADS_Internal(MPI_Comm comm, ego context, ego model, DM *newdm)
275 {
276   DMLabel  bodyLabel, faceLabel, edgeLabel, vertexLabel;
277   PetscInt cStart, cEnd, c;
278   /* EGADSLite variables */
279   ego geom, *bodies, *objs, *nobjs, *mobjs, *lobjs;
280   int oclass, mtype, nbodies, *senses;
281   int b;
282   /* PETSc variables */
283   DM          dm;
284   PetscHMapI  edgeMap = NULL;
285   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;
286   PetscInt   *cells = NULL, *cone = NULL;
287   PetscReal  *coords = NULL;
288   PetscMPIInt rank;
289 
290   PetscFunctionBegin;
291   PetscCallMPI(MPI_Comm_rank(comm, &rank));
292   if (rank == 0) {
293     const PetscInt debug = 0;
294 
295     /* ---------------------------------------------------------------------------------------------------
296     Generate Petsc Plex
297       Get all Nodes in model, record coordinates in a correctly formatted array
298       Cycle through bodies, cycle through loops, recorde NODE IDs in a correctly formatted array
299       We need to uniformly refine the initial geometry to guarantee a valid mesh
300     */
301 
302     /* Calculate cell and vertex sizes */
303     PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &nbodies, &bodies, &senses));
304     PetscCall(PetscHMapICreate(&edgeMap));
305     numEdges = 0;
306     for (b = 0; b < nbodies; ++b) {
307       ego body = bodies[b];
308       int id, Nl, l, Nv, v;
309 
310       PetscCall(EG_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
311       for (l = 0; l < Nl; ++l) {
312         ego loop = lobjs[l];
313         int Ner  = 0, Ne, e, Nc;
314 
315         PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));
316         for (e = 0; e < Ne; ++e) {
317           ego           edge = objs[e];
318           int           Nv, id;
319           PetscHashIter iter;
320           PetscBool     found;
321 
322           PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
323           if (mtype == DEGENERATE) continue;
324           id = EG_indexBodyTopo(body, edge);
325           PetscCall(PetscHMapIFind(edgeMap, id - 1, &iter, &found));
326           if (!found) PetscCall(PetscHMapISet(edgeMap, id - 1, numEdges++));
327           ++Ner;
328         }
329         if (Ner == 2) {
330           Nc = 2;
331         } else if (Ner == 3) {
332           Nc = 4;
333         } else if (Ner == 4) {
334           Nc = 8;
335           ++numQuads;
336         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot support loop with %d edges", Ner);
337         numCells += Nc;
338         newCells += Nc - 1;
339         maxCorners = PetscMax(Ner * 2 + 1, maxCorners);
340       }
341       PetscCall(EG_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
342       for (v = 0; v < Nv; ++v) {
343         ego vertex = nobjs[v];
344 
345         id = EG_indexBodyTopo(body, vertex);
346         /* TODO: Instead of assuming contiguous ids, we could use a hash table */
347         numVertices = PetscMax(id, numVertices);
348       }
349       EG_free(lobjs);
350       EG_free(nobjs);
351     }
352     PetscCall(PetscHMapIGetSize(edgeMap, &numEdges));
353     newVertices = numEdges + numQuads;
354     numVertices += newVertices;
355 
356     dim        = 2; /* Assume 3D Models :: Need to update to handle 2D Models in the future */
357     cdim       = 3; /* Assume 3D Models :: Need to update to handle 2D Models in the future */
358     numCorners = 3; /* Split cells into triangles */
359     PetscCall(PetscMalloc3(numVertices * cdim, &coords, numCells * numCorners, &cells, maxCorners, &cone));
360 
361     /* Get vertex coordinates */
362     for (b = 0; b < nbodies; ++b) {
363       ego body = bodies[b];
364       int id, Nv, v;
365 
366       PetscCall(EG_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
367       for (v = 0; v < Nv; ++v) {
368         ego    vertex = nobjs[v];
369         double limits[4];
370         int    dummy;
371 
372         PetscCall(EG_getTopology(vertex, &geom, &oclass, &mtype, limits, &dummy, &mobjs, &senses));
373         id                          = EG_indexBodyTopo(body, vertex);
374         coords[(id - 1) * cdim + 0] = limits[0];
375         coords[(id - 1) * cdim + 1] = limits[1];
376         coords[(id - 1) * cdim + 2] = limits[2];
377       }
378       EG_free(nobjs);
379     }
380     PetscCall(PetscHMapIClear(edgeMap));
381     fOff     = numVertices - newVertices + numEdges;
382     numEdges = 0;
383     numQuads = 0;
384     for (b = 0; b < nbodies; ++b) {
385       ego body = bodies[b];
386       int Nl, l;
387 
388       PetscCall(EG_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
389       for (l = 0; l < Nl; ++l) {
390         ego loop = lobjs[l];
391         int lid, Ner = 0, Ne, e;
392 
393         lid = EG_indexBodyTopo(body, loop);
394         PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));
395         for (e = 0; e < Ne; ++e) {
396           ego           edge = objs[e];
397           int           eid, Nv;
398           PetscHashIter iter;
399           PetscBool     found;
400 
401           PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
402           if (mtype == DEGENERATE) continue;
403           ++Ner;
404           eid = EG_indexBodyTopo(body, edge);
405           PetscCall(PetscHMapIFind(edgeMap, eid - 1, &iter, &found));
406           if (!found) {
407             PetscInt v = numVertices - newVertices + numEdges;
408             double   range[4], params[3] = {0., 0., 0.}, result[18];
409             int      periodic[2];
410 
411             PetscCall(PetscHMapISet(edgeMap, eid - 1, numEdges++));
412             PetscCall(EG_getRange(edge, range, periodic));
413             params[0] = 0.5 * (range[0] + range[1]);
414             PetscCall(EG_evaluate(edge, params, result));
415             coords[v * cdim + 0] = result[0];
416             coords[v * cdim + 1] = result[1];
417             coords[v * cdim + 2] = result[2];
418           }
419         }
420         if (Ner == 4) {
421           PetscInt v = fOff + numQuads++;
422           ego     *fobjs, face;
423           double   range[4], params[3] = {0., 0., 0.}, result[18];
424           int      Nf, fid, periodic[2];
425 
426           PetscCall(EG_getBodyTopos(body, loop, FACE, &Nf, &fobjs));
427           face = fobjs[0];
428           fid  = EG_indexBodyTopo(body, face);
429           PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Loop %d has %d faces, instead of 1 (%d)", lid - 1, Nf, fid);
430           PetscCall(EG_getRange(face, range, periodic));
431           params[0] = 0.5 * (range[0] + range[1]);
432           params[1] = 0.5 * (range[2] + range[3]);
433           PetscCall(EG_evaluate(face, params, result));
434           coords[v * cdim + 0] = result[0];
435           coords[v * cdim + 1] = result[1];
436           coords[v * cdim + 2] = result[2];
437         }
438       }
439     }
440     PetscCheck(numEdges + numQuads == newVertices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of new vertices %" PetscInt_FMT " != %" PetscInt_FMT " previous count", numEdges + numQuads, newVertices);
441 
442     /* Get cell vertices by traversing loops */
443     numQuads = 0;
444     cOff     = 0;
445     for (b = 0; b < nbodies; ++b) {
446       ego body = bodies[b];
447       int id, Nl, l;
448 
449       PetscCall(EG_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
450       for (l = 0; l < Nl; ++l) {
451         ego loop = lobjs[l];
452         int lid, Ner = 0, Ne, e, nc = 0, c, Nt, t;
453 
454         lid = EG_indexBodyTopo(body, loop);
455         PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));
456 
457         for (e = 0; e < Ne; ++e) {
458           ego edge = objs[e];
459           int points[3];
460           int eid, Nv, v, tmp;
461 
462           eid = EG_indexBodyTopo(body, edge);
463           PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
464           if (mtype == DEGENERATE) continue;
465           else ++Ner;
466           PetscCheck(Nv == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Edge %d has %d vertices != 2", eid, Nv);
467 
468           for (v = 0; v < Nv; ++v) {
469             ego vertex = nobjs[v];
470 
471             id            = EG_indexBodyTopo(body, vertex);
472             points[v * 2] = id - 1;
473           }
474           {
475             PetscInt edgeNum;
476 
477             PetscCall(PetscHMapIGet(edgeMap, eid - 1, &edgeNum));
478             points[1] = numVertices - newVertices + edgeNum;
479           }
480           /* EGADS loops are not oriented, but seem to be in order, so we must piece them together */
481           if (!nc) {
482             for (v = 0; v < Nv + 1; ++v) cone[nc++] = points[v];
483           } else {
484             if (cone[nc - 1] == points[0]) {
485               cone[nc++] = points[1];
486               if (cone[0] != points[2]) cone[nc++] = points[2];
487             } else if (cone[nc - 1] == points[2]) {
488               cone[nc++] = points[1];
489               if (cone[0] != points[0]) cone[nc++] = points[0];
490             } else if (cone[nc - 3] == points[0]) {
491               tmp          = cone[nc - 3];
492               cone[nc - 3] = cone[nc - 1];
493               cone[nc - 1] = tmp;
494               cone[nc++]   = points[1];
495               if (cone[0] != points[2]) cone[nc++] = points[2];
496             } else if (cone[nc - 3] == points[2]) {
497               tmp          = cone[nc - 3];
498               cone[nc - 3] = cone[nc - 1];
499               cone[nc - 1] = tmp;
500               cone[nc++]   = points[1];
501               if (cone[0] != points[0]) cone[nc++] = points[0];
502             } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Edge %d does not match its predecessor", eid);
503           }
504         }
505         PetscCheck(nc == 2 * Ner, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Number of corners %" PetscInt_FMT " != %" PetscInt_FMT, nc, 2 * Ner);
506         if (Ner == 4) cone[nc++] = numVertices - newVertices + numEdges + numQuads++;
507         PetscCheck(nc <= maxCorners, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Number of corners %" PetscInt_FMT " > %" PetscInt_FMT " max", nc, maxCorners);
508         /* Triangulate the loop */
509         switch (Ner) {
510         case 2: /* Bi-Segment -> 2 triangles */
511           Nt                           = 2;
512           cells[cOff * numCorners + 0] = cone[0];
513           cells[cOff * numCorners + 1] = cone[1];
514           cells[cOff * numCorners + 2] = cone[2];
515           ++cOff;
516           cells[cOff * numCorners + 0] = cone[0];
517           cells[cOff * numCorners + 1] = cone[2];
518           cells[cOff * numCorners + 2] = cone[3];
519           ++cOff;
520           break;
521         case 3: /* Triangle   -> 4 triangles */
522           Nt                           = 4;
523           cells[cOff * numCorners + 0] = cone[0];
524           cells[cOff * numCorners + 1] = cone[1];
525           cells[cOff * numCorners + 2] = cone[5];
526           ++cOff;
527           cells[cOff * numCorners + 0] = cone[1];
528           cells[cOff * numCorners + 1] = cone[2];
529           cells[cOff * numCorners + 2] = cone[3];
530           ++cOff;
531           cells[cOff * numCorners + 0] = cone[5];
532           cells[cOff * numCorners + 1] = cone[3];
533           cells[cOff * numCorners + 2] = cone[4];
534           ++cOff;
535           cells[cOff * numCorners + 0] = cone[1];
536           cells[cOff * numCorners + 1] = cone[3];
537           cells[cOff * numCorners + 2] = cone[5];
538           ++cOff;
539           break;
540         case 4: /* Quad       -> 8 triangles */
541           Nt                           = 8;
542           cells[cOff * numCorners + 0] = cone[0];
543           cells[cOff * numCorners + 1] = cone[1];
544           cells[cOff * numCorners + 2] = cone[7];
545           ++cOff;
546           cells[cOff * numCorners + 0] = cone[1];
547           cells[cOff * numCorners + 1] = cone[2];
548           cells[cOff * numCorners + 2] = cone[3];
549           ++cOff;
550           cells[cOff * numCorners + 0] = cone[3];
551           cells[cOff * numCorners + 1] = cone[4];
552           cells[cOff * numCorners + 2] = cone[5];
553           ++cOff;
554           cells[cOff * numCorners + 0] = cone[5];
555           cells[cOff * numCorners + 1] = cone[6];
556           cells[cOff * numCorners + 2] = cone[7];
557           ++cOff;
558           cells[cOff * numCorners + 0] = cone[8];
559           cells[cOff * numCorners + 1] = cone[1];
560           cells[cOff * numCorners + 2] = cone[3];
561           ++cOff;
562           cells[cOff * numCorners + 0] = cone[8];
563           cells[cOff * numCorners + 1] = cone[3];
564           cells[cOff * numCorners + 2] = cone[5];
565           ++cOff;
566           cells[cOff * numCorners + 0] = cone[8];
567           cells[cOff * numCorners + 1] = cone[5];
568           cells[cOff * numCorners + 2] = cone[7];
569           ++cOff;
570           cells[cOff * numCorners + 0] = cone[8];
571           cells[cOff * numCorners + 1] = cone[7];
572           cells[cOff * numCorners + 2] = cone[1];
573           ++cOff;
574           break;
575         default:
576           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Loop %d has %d edges, which we do not support", lid, Ner);
577         }
578         if (debug) {
579           for (t = 0; t < Nt; ++t) {
580             PetscCall(PetscPrintf(PETSC_COMM_SELF, "  LOOP Corner NODEs Triangle %" PetscInt_FMT " (", t));
581             for (c = 0; c < numCorners; ++c) {
582               if (c > 0) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
583               PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT, cells[(cOff - Nt + t) * numCorners + c]));
584             }
585             PetscCall(PetscPrintf(PETSC_COMM_SELF, ")\n"));
586           }
587         }
588       }
589       EG_free(lobjs);
590     }
591   }
592   PetscCheck(cOff == numCells, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Count of total cells %" PetscInt_FMT " != %" PetscInt_FMT " previous count", cOff, numCells);
593   PetscCall(DMPlexCreateFromCellListPetsc(PETSC_COMM_WORLD, dim, numCells, numVertices, numCorners, PETSC_TRUE, cells, cdim, coords, &dm));
594   PetscCall(PetscFree3(coords, cells, cone));
595   PetscCall(PetscInfo(dm, " Total Number of Unique Cells    = %" PetscInt_FMT " (%" PetscInt_FMT ")\n", numCells, newCells));
596   PetscCall(PetscInfo(dm, " Total Number of Unique Vertices = %" PetscInt_FMT " (%" PetscInt_FMT ")\n", numVertices, newVertices));
597   /* Embed EGADS model in DM */
598   {
599     PetscContainer modelObj, contextObj;
600 
601     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &modelObj));
602     PetscCall(PetscContainerSetPointer(modelObj, model));
603     PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Model", (PetscObject)modelObj));
604     PetscCall(PetscContainerDestroy(&modelObj));
605 
606     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &contextObj));
607     PetscCall(PetscContainerSetPointer(contextObj, context));
608     PetscCall(PetscContainerSetUserDestroy(contextObj, DMPlexEGADSDestroy_Private));
609     PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Context", (PetscObject)contextObj));
610     PetscCall(PetscContainerDestroy(&contextObj));
611   }
612   /* Label points */
613   PetscCall(DMCreateLabel(dm, "EGADS Body ID"));
614   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
615   PetscCall(DMCreateLabel(dm, "EGADS Face ID"));
616   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
617   PetscCall(DMCreateLabel(dm, "EGADS Edge ID"));
618   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
619   PetscCall(DMCreateLabel(dm, "EGADS Vertex ID"));
620   PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));
621   cOff = 0;
622   for (b = 0; b < nbodies; ++b) {
623     ego body = bodies[b];
624     int id, Nl, l;
625 
626     PetscCall(EG_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
627     for (l = 0; l < Nl; ++l) {
628       ego  loop = lobjs[l];
629       ego *fobjs;
630       int  lid, Nf, fid, Ner = 0, Ne, e, Nt = 0, t;
631 
632       lid = EG_indexBodyTopo(body, loop);
633       PetscCall(EG_getBodyTopos(body, loop, FACE, &Nf, &fobjs));
634       PetscCheck(Nf <= 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Loop %d has %d > 1 faces, which is not supported", lid, Nf);
635       fid = EG_indexBodyTopo(body, fobjs[0]);
636       EG_free(fobjs);
637       PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));
638       for (e = 0; e < Ne; ++e) {
639         ego             edge = objs[e];
640         int             eid, Nv, v;
641         PetscInt        points[3], support[2], numEdges, edgeNum;
642         const PetscInt *edges;
643 
644         eid = EG_indexBodyTopo(body, edge);
645         PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
646         if (mtype == DEGENERATE) continue;
647         else ++Ner;
648         for (v = 0; v < Nv; ++v) {
649           ego vertex = nobjs[v];
650 
651           id = EG_indexBodyTopo(body, vertex);
652           PetscCall(DMLabelSetValue(edgeLabel, numCells + id - 1, eid));
653           points[v * 2] = numCells + id - 1;
654         }
655         PetscCall(PetscHMapIGet(edgeMap, eid - 1, &edgeNum));
656         points[1] = numCells + numVertices - newVertices + edgeNum;
657 
658         PetscCall(DMLabelSetValue(edgeLabel, points[1], eid));
659         support[0] = points[0];
660         support[1] = points[1];
661         PetscCall(DMPlexGetJoin(dm, 2, support, &numEdges, &edges));
662         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);
663         PetscCall(DMLabelSetValue(edgeLabel, edges[0], eid));
664         PetscCall(DMPlexRestoreJoin(dm, 2, support, &numEdges, &edges));
665         support[0] = points[1];
666         support[1] = points[2];
667         PetscCall(DMPlexGetJoin(dm, 2, support, &numEdges, &edges));
668         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);
669         PetscCall(DMLabelSetValue(edgeLabel, edges[0], eid));
670         PetscCall(DMPlexRestoreJoin(dm, 2, support, &numEdges, &edges));
671       }
672       switch (Ner) {
673       case 2:
674         Nt = 2;
675         break;
676       case 3:
677         Nt = 4;
678         break;
679       case 4:
680         Nt = 8;
681         break;
682       default:
683         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Loop with %d edges is unsupported", Ner);
684       }
685       for (t = 0; t < Nt; ++t) {
686         PetscCall(DMLabelSetValue(bodyLabel, cOff + t, b));
687         PetscCall(DMLabelSetValue(faceLabel, cOff + t, fid));
688       }
689       cOff += Nt;
690     }
691     EG_free(lobjs);
692   }
693   PetscCall(PetscHMapIDestroy(&edgeMap));
694   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
695   for (c = cStart; c < cEnd; ++c) {
696     PetscInt *closure = NULL;
697     PetscInt  clSize, cl, bval, fval;
698 
699     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
700     PetscCall(DMLabelGetValue(bodyLabel, c, &bval));
701     PetscCall(DMLabelGetValue(faceLabel, c, &fval));
702     for (cl = 0; cl < clSize * 2; cl += 2) {
703       PetscCall(DMLabelSetValue(bodyLabel, closure[cl], bval));
704       PetscCall(DMLabelSetValue(faceLabel, closure[cl], fval));
705     }
706     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
707   }
708   *newdm = dm;
709   PetscFunctionReturn(PETSC_SUCCESS);
710 }
711 
712 static PetscErrorCode DMPlexCreateEGADS(MPI_Comm comm, ego context, ego model, DM *newdm)
713 {
714   DMLabel bodyLabel, faceLabel, edgeLabel, vertexLabel;
715   // EGADS/EGADSLite variables
716   ego geom, *bodies, *mobjs, *fobjs, *lobjs, *eobjs, *nobjs;
717   ego topRef, prev, next;
718   int oclass, mtype, nbodies, *senses, *lSenses, *eSenses;
719   int b;
720   // PETSc variables
721   DM              dm;
722   PetscHMapI      edgeMap = NULL, bodyIndexMap = NULL, bodyVertexMap = NULL, bodyEdgeMap = NULL, bodyFaceMap = NULL, bodyEdgeGlobalMap = NULL;
723   PetscInt        dim = -1, cdim = -1, numCorners = 0, numVertices = 0, numEdges = 0, numFaces = 0, numCells = 0, edgeCntr = 0;
724   PetscInt        cellCntr = 0, numPoints = 0;
725   PetscInt       *cells  = NULL;
726   const PetscInt *cone   = NULL;
727   PetscReal      *coords = NULL;
728   PetscMPIInt     rank;
729 
730   PetscFunctionBeginUser;
731   PetscCallMPI(MPI_Comm_rank(comm, &rank));
732   if (rank == 0) {
733     // ---------------------------------------------------------------------------------------------------
734     // Generate Petsc Plex
735     //  Get all Nodes in model, record coordinates in a correctly formatted array
736     //  Cycle through bodies, cycle through loops, recorde NODE IDs in a correctly formatted array
737     //  We need to uniformly refine the initial geometry to guarantee a valid mesh
738 
739     // Calculate cell and vertex sizes
740     PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &nbodies, &bodies, &senses));
741 
742     PetscCall(PetscHMapICreate(&edgeMap));
743     PetscCall(PetscHMapICreate(&bodyIndexMap));
744     PetscCall(PetscHMapICreate(&bodyVertexMap));
745     PetscCall(PetscHMapICreate(&bodyEdgeMap));
746     PetscCall(PetscHMapICreate(&bodyEdgeGlobalMap));
747     PetscCall(PetscHMapICreate(&bodyFaceMap));
748 
749     for (b = 0; b < nbodies; ++b) {
750       ego           body = bodies[b];
751       int           Nf, Ne, Nv;
752       PetscHashIter BIiter, BViter, BEiter, BEGiter, BFiter, EMiter;
753       PetscBool     BIfound, BVfound, BEfound, BEGfound, BFfound, EMfound;
754 
755       PetscCall(PetscHMapIFind(bodyIndexMap, b, &BIiter, &BIfound));
756       PetscCall(PetscHMapIFind(bodyVertexMap, b, &BViter, &BVfound));
757       PetscCall(PetscHMapIFind(bodyEdgeMap, b, &BEiter, &BEfound));
758       PetscCall(PetscHMapIFind(bodyEdgeGlobalMap, b, &BEGiter, &BEGfound));
759       PetscCall(PetscHMapIFind(bodyFaceMap, b, &BFiter, &BFfound));
760 
761       if (!BIfound) PetscCall(PetscHMapISet(bodyIndexMap, b, numFaces + numEdges + numVertices));
762       if (!BVfound) PetscCall(PetscHMapISet(bodyVertexMap, b, numVertices));
763       if (!BEfound) PetscCall(PetscHMapISet(bodyEdgeMap, b, numEdges));
764       if (!BEGfound) PetscCall(PetscHMapISet(bodyEdgeGlobalMap, b, edgeCntr));
765       if (!BFfound) PetscCall(PetscHMapISet(bodyFaceMap, b, numFaces));
766 
767       PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
768       PetscCall(EG_getBodyTopos(body, NULL, EDGE, &Ne, &eobjs));
769       PetscCall(EG_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
770       EG_free(fobjs);
771       EG_free(eobjs);
772       EG_free(nobjs);
773 
774       // Remove DEGENERATE EDGES from Edge count
775       PetscCall(EG_getBodyTopos(body, NULL, EDGE, &Ne, &eobjs));
776       int Netemp = 0;
777       for (int e = 0; e < Ne; ++e) {
778         ego edge = eobjs[e];
779         int eid;
780 
781         PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
782         eid = EG_indexBodyTopo(body, edge);
783 
784         PetscCall(PetscHMapIFind(edgeMap, edgeCntr + eid - 1, &EMiter, &EMfound));
785         if (mtype == DEGENERATE) {
786           if (!EMfound) PetscCall(PetscHMapISet(edgeMap, edgeCntr + eid - 1, -1));
787         } else {
788           ++Netemp;
789           if (!EMfound) PetscCall(PetscHMapISet(edgeMap, edgeCntr + eid - 1, Netemp));
790         }
791       }
792       EG_free(eobjs);
793 
794       // Determine Number of Cells
795       PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
796       for (int f = 0; f < Nf; ++f) {
797         ego face     = fobjs[f];
798         int edgeTemp = 0;
799 
800         PetscCall(EG_getBodyTopos(body, face, EDGE, &Ne, &eobjs));
801         for (int e = 0; e < Ne; ++e) {
802           ego edge = eobjs[e];
803 
804           PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
805           if (mtype != DEGENERATE) ++edgeTemp;
806         }
807         numCells += (2 * edgeTemp);
808         EG_free(eobjs);
809       }
810       EG_free(fobjs);
811 
812       numFaces += Nf;
813       numEdges += Netemp;
814       numVertices += Nv;
815       edgeCntr += Ne;
816     }
817 
818     // Set up basic DMPlex parameters
819     dim        = 2;                                 // Assumes 3D Models :: Need to handle 2D models in the future
820     cdim       = 3;                                 // Assumes 3D Models :: Need to update to handle 2D models in future
821     numCorners = 3;                                 // Split Faces into triangles
822     numPoints  = numVertices + numEdges + numFaces; // total number of coordinate points
823 
824     PetscCall(PetscMalloc2(numPoints * cdim, &coords, numCells * numCorners, &cells));
825 
826     // Get Vertex Coordinates and Set up Cells
827     for (b = 0; b < nbodies; ++b) {
828       ego           body = bodies[b];
829       int           Nf, Ne, Nv;
830       PetscInt      bodyVertexIndexStart, bodyEdgeIndexStart, bodyEdgeGlobalIndexStart, bodyFaceIndexStart;
831       PetscHashIter BViter, BEiter, BEGiter, BFiter, EMiter;
832       PetscBool     BVfound, BEfound, BEGfound, BFfound, EMfound;
833 
834       // Vertices on Current Body
835       PetscCall(EG_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
836 
837       PetscCall(PetscHMapIFind(bodyVertexMap, b, &BViter, &BVfound));
838       PetscCheck(BVfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in bodyVertexMap", b);
839       PetscCall(PetscHMapIGet(bodyVertexMap, b, &bodyVertexIndexStart));
840 
841       for (int v = 0; v < Nv; ++v) {
842         ego    vertex = nobjs[v];
843         double limits[4];
844         int    id, dummy;
845 
846         PetscCall(EG_getTopology(vertex, &geom, &oclass, &mtype, limits, &dummy, &mobjs, &senses));
847         id = EG_indexBodyTopo(body, vertex);
848 
849         coords[(bodyVertexIndexStart + id - 1) * cdim + 0] = limits[0];
850         coords[(bodyVertexIndexStart + id - 1) * cdim + 1] = limits[1];
851         coords[(bodyVertexIndexStart + id - 1) * cdim + 2] = limits[2];
852       }
853       EG_free(nobjs);
854 
855       // Edge Midpoint Vertices on Current Body
856       PetscCall(EG_getBodyTopos(body, NULL, EDGE, &Ne, &eobjs));
857 
858       PetscCall(PetscHMapIFind(bodyEdgeMap, b, &BEiter, &BEfound));
859       PetscCheck(BEfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in bodyEdgeMap", b);
860       PetscCall(PetscHMapIGet(bodyEdgeMap, b, &bodyEdgeIndexStart));
861 
862       PetscCall(PetscHMapIFind(bodyEdgeGlobalMap, b, &BEGiter, &BEGfound));
863       PetscCheck(BEGfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in bodyEdgeGlobalMap", b);
864       PetscCall(PetscHMapIGet(bodyEdgeGlobalMap, b, &bodyEdgeGlobalIndexStart));
865 
866       for (int e = 0; e < Ne; ++e) {
867         ego    edge = eobjs[e];
868         double range[2], avgt[1], cntrPnt[9];
869         int    eid, eOffset;
870         int    periodic;
871 
872         PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
873         if (mtype == DEGENERATE) continue;
874 
875         eid = EG_indexBodyTopo(body, edge);
876 
877         // get relative offset from globalEdgeID Vector
878         PetscCall(PetscHMapIFind(edgeMap, bodyEdgeGlobalIndexStart + eid - 1, &EMiter, &EMfound));
879         PetscCheck(EMfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Edge %d not found in edgeMap", bodyEdgeGlobalIndexStart + eid - 1);
880         PetscCall(PetscHMapIGet(edgeMap, bodyEdgeGlobalIndexStart + eid - 1, &eOffset));
881 
882         PetscCall(EG_getRange(edge, range, &periodic));
883         avgt[0] = (range[0] + range[1]) / 2.;
884 
885         PetscCall(EG_evaluate(edge, avgt, cntrPnt));
886         coords[(numVertices + bodyEdgeIndexStart + eOffset - 1) * cdim + 0] = cntrPnt[0];
887         coords[(numVertices + bodyEdgeIndexStart + eOffset - 1) * cdim + 1] = cntrPnt[1];
888         coords[(numVertices + bodyEdgeIndexStart + eOffset - 1) * cdim + 2] = cntrPnt[2];
889       }
890       EG_free(eobjs);
891 
892       // Face Midpoint Vertices on Current Body
893       PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
894 
895       PetscCall(PetscHMapIFind(bodyFaceMap, b, &BFiter, &BFfound));
896       PetscCheck(BFfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in bodyFaceMap", b);
897       PetscCall(PetscHMapIGet(bodyFaceMap, b, &bodyFaceIndexStart));
898 
899       for (int f = 0; f < Nf; ++f) {
900         ego    face = fobjs[f];
901         double range[4], avgUV[2], cntrPnt[18];
902         int    peri, id;
903 
904         id = EG_indexBodyTopo(body, face);
905         PetscCall(EG_getRange(face, range, &peri));
906 
907         avgUV[0] = (range[0] + range[1]) / 2.;
908         avgUV[1] = (range[2] + range[3]) / 2.;
909         PetscCall(EG_evaluate(face, avgUV, cntrPnt));
910 
911         coords[(numVertices + numEdges + bodyFaceIndexStart + id - 1) * cdim + 0] = cntrPnt[0];
912         coords[(numVertices + numEdges + bodyFaceIndexStart + id - 1) * cdim + 1] = cntrPnt[1];
913         coords[(numVertices + numEdges + bodyFaceIndexStart + id - 1) * cdim + 2] = cntrPnt[2];
914       }
915       EG_free(fobjs);
916 
917       // Define Cells :: Note - This could be incorporated in the Face Midpoint Vertices Loop but was kept separate for clarity
918       PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
919       for (int f = 0; f < Nf; ++f) {
920         ego face = fobjs[f];
921         int fID, midFaceID, midPntID, startID, endID, Nl;
922 
923         fID       = EG_indexBodyTopo(body, face);
924         midFaceID = numVertices + numEdges + bodyFaceIndexStart + fID - 1;
925         // Must Traverse Loop to ensure we have all necessary information like the sense (+/- 1) of the edges.
926         // TODO :: Only handles single loop faces (No holes). The choices for handling multiloop faces are:
927         //            1) Use the DMPlexCreateEGADSFromFile() with the -dm_plex_egads_with_tess = 1 option.
928         //               This will use a default EGADS tessellation as an initial surface mesh.
929         //            2) Create the initial surface mesh via a 2D mesher :: Currently not available (?future?)
930         //               May I suggest the XXXX as a starting point?
931 
932         PetscCall(EG_getTopology(face, &geom, &oclass, &mtype, NULL, &Nl, &lobjs, &lSenses));
933 
934         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);
935         for (int l = 0; l < Nl; ++l) {
936           ego loop = lobjs[l];
937 
938           PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &eobjs, &eSenses));
939           for (int e = 0; e < Ne; ++e) {
940             ego edge = eobjs[e];
941             int eid, eOffset;
942 
943             PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
944             eid = EG_indexBodyTopo(body, edge);
945             if (mtype == DEGENERATE) continue;
946 
947             // get relative offset from globalEdgeID Vector
948             PetscCall(PetscHMapIFind(edgeMap, bodyEdgeGlobalIndexStart + eid - 1, &EMiter, &EMfound));
949             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);
950             PetscCall(PetscHMapIGet(edgeMap, bodyEdgeGlobalIndexStart + eid - 1, &eOffset));
951 
952             midPntID = numVertices + bodyEdgeIndexStart + eOffset - 1;
953 
954             PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
955 
956             if (eSenses[e] > 0) {
957               startID = EG_indexBodyTopo(body, nobjs[0]);
958               endID   = EG_indexBodyTopo(body, nobjs[1]);
959             } else {
960               startID = EG_indexBodyTopo(body, nobjs[1]);
961               endID   = EG_indexBodyTopo(body, nobjs[0]);
962             }
963 
964             // Define 2 Cells per Edge with correct orientation
965             cells[cellCntr * numCorners + 0] = midFaceID;
966             cells[cellCntr * numCorners + 1] = bodyVertexIndexStart + startID - 1;
967             cells[cellCntr * numCorners + 2] = midPntID;
968 
969             cells[cellCntr * numCorners + 3] = midFaceID;
970             cells[cellCntr * numCorners + 4] = midPntID;
971             cells[cellCntr * numCorners + 5] = bodyVertexIndexStart + endID - 1;
972 
973             cellCntr = cellCntr + 2;
974           }
975         }
976       }
977       EG_free(fobjs);
978     }
979   }
980 
981   // Generate DMPlex
982   PetscCall(DMPlexCreateFromCellListPetsc(PETSC_COMM_WORLD, dim, numCells, numPoints, numCorners, PETSC_TRUE, cells, cdim, coords, &dm));
983   PetscCall(PetscFree2(coords, cells));
984   PetscCall(PetscInfo(dm, " Total Number of Unique Cells    = %" PetscInt_FMT " \n", numCells));
985   PetscCall(PetscInfo(dm, " Total Number of Unique Vertices = %" PetscInt_FMT " \n", numVertices));
986 
987   // Embed EGADS model in DM
988   {
989     PetscContainer modelObj, contextObj;
990 
991     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &modelObj));
992     PetscCall(PetscContainerSetPointer(modelObj, model));
993     PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Model", (PetscObject)modelObj));
994     PetscCall(PetscContainerDestroy(&modelObj));
995 
996     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &contextObj));
997     PetscCall(PetscContainerSetPointer(contextObj, context));
998     PetscCall(PetscContainerSetUserDestroy(contextObj, DMPlexEGADSDestroy_Private));
999     PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Context", (PetscObject)contextObj));
1000     PetscCall(PetscContainerDestroy(&contextObj));
1001   }
1002   // Label points
1003   PetscInt nStart, nEnd;
1004 
1005   PetscCall(DMCreateLabel(dm, "EGADS Body ID"));
1006   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
1007   PetscCall(DMCreateLabel(dm, "EGADS Face ID"));
1008   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
1009   PetscCall(DMCreateLabel(dm, "EGADS Edge ID"));
1010   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
1011   PetscCall(DMCreateLabel(dm, "EGADS Vertex ID"));
1012   PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));
1013 
1014   PetscCall(DMPlexGetHeightStratum(dm, 2, &nStart, &nEnd));
1015 
1016   cellCntr = 0;
1017   for (b = 0; b < nbodies; ++b) {
1018     ego           body = bodies[b];
1019     int           Nv, Ne, Nf;
1020     PetscInt      bodyVertexIndexStart, bodyEdgeIndexStart, bodyEdgeGlobalIndexStart, bodyFaceIndexStart;
1021     PetscHashIter BViter, BEiter, BEGiter, BFiter, EMiter;
1022     PetscBool     BVfound, BEfound, BEGfound, BFfound, EMfound;
1023 
1024     PetscCall(PetscHMapIFind(bodyVertexMap, b, &BViter, &BVfound));
1025     PetscCheck(BVfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in bodyVertexMap", b);
1026     PetscCall(PetscHMapIGet(bodyVertexMap, b, &bodyVertexIndexStart));
1027 
1028     PetscCall(PetscHMapIFind(bodyEdgeMap, b, &BEiter, &BEfound));
1029     PetscCheck(BEfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in bodyEdgeMap", b);
1030     PetscCall(PetscHMapIGet(bodyEdgeMap, b, &bodyEdgeIndexStart));
1031 
1032     PetscCall(PetscHMapIFind(bodyFaceMap, b, &BFiter, &BFfound));
1033     PetscCheck(BFfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in bodyFaceMap", b);
1034     PetscCall(PetscHMapIGet(bodyFaceMap, b, &bodyFaceIndexStart));
1035 
1036     PetscCall(PetscHMapIFind(bodyEdgeGlobalMap, b, &BEGiter, &BEGfound));
1037     PetscCheck(BEGfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in bodyEdgeGlobalMap", b);
1038     PetscCall(PetscHMapIGet(bodyEdgeGlobalMap, b, &bodyEdgeGlobalIndexStart));
1039 
1040     PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
1041     for (int f = 0; f < Nf; ++f) {
1042       ego face = fobjs[f];
1043       int fID, Nl;
1044 
1045       fID = EG_indexBodyTopo(body, face);
1046 
1047       PetscCall(EG_getBodyTopos(body, face, LOOP, &Nl, &lobjs));
1048       for (int l = 0; l < Nl; ++l) {
1049         ego loop = lobjs[l];
1050         int lid;
1051 
1052         lid = EG_indexBodyTopo(body, loop);
1053         PetscCheck(Nl <= 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Loop %d has %d > 1 faces, which is not supported", lid, Nf);
1054 
1055         PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &eobjs, &eSenses));
1056         for (int e = 0; e < Ne; ++e) {
1057           ego edge = eobjs[e];
1058           int eid, eOffset;
1059 
1060           // Skip DEGENERATE Edges
1061           PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
1062           if (mtype == DEGENERATE) continue;
1063           eid = EG_indexBodyTopo(body, edge);
1064 
1065           // get relative offset from globalEdgeID Vector
1066           PetscCall(PetscHMapIFind(edgeMap, bodyEdgeGlobalIndexStart + eid - 1, &EMiter, &EMfound));
1067           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);
1068           PetscCall(PetscHMapIGet(edgeMap, bodyEdgeGlobalIndexStart + eid - 1, &eOffset));
1069 
1070           PetscCall(EG_getBodyTopos(body, edge, NODE, &Nv, &nobjs));
1071           for (int v = 0; v < Nv; ++v) {
1072             ego vertex = nobjs[v];
1073             int vID;
1074 
1075             vID = EG_indexBodyTopo(body, vertex);
1076             PetscCall(DMLabelSetValue(bodyLabel, nStart + bodyVertexIndexStart + vID - 1, b));
1077             PetscCall(DMLabelSetValue(vertexLabel, nStart + bodyVertexIndexStart + vID - 1, vID));
1078           }
1079           EG_free(nobjs);
1080 
1081           PetscCall(DMLabelSetValue(bodyLabel, nStart + numVertices + bodyEdgeIndexStart + eOffset - 1, b));
1082           PetscCall(DMLabelSetValue(edgeLabel, nStart + numVertices + bodyEdgeIndexStart + eOffset - 1, eid));
1083 
1084           // Define Cell faces
1085           for (int jj = 0; jj < 2; ++jj) {
1086             PetscCall(DMLabelSetValue(bodyLabel, cellCntr, b));
1087             PetscCall(DMLabelSetValue(faceLabel, cellCntr, fID));
1088             PetscCall(DMPlexGetCone(dm, cellCntr, &cone));
1089 
1090             PetscCall(DMLabelSetValue(bodyLabel, cone[0], b));
1091             PetscCall(DMLabelSetValue(faceLabel, cone[0], fID));
1092 
1093             PetscCall(DMLabelSetValue(bodyLabel, cone[1], b));
1094             PetscCall(DMLabelSetValue(edgeLabel, cone[1], eid));
1095 
1096             PetscCall(DMLabelSetValue(bodyLabel, cone[2], b));
1097             PetscCall(DMLabelSetValue(faceLabel, cone[2], fID));
1098 
1099             cellCntr = cellCntr + 1;
1100           }
1101         }
1102       }
1103       EG_free(lobjs);
1104 
1105       PetscCall(DMLabelSetValue(bodyLabel, nStart + numVertices + numEdges + bodyFaceIndexStart + fID - 1, b));
1106       PetscCall(DMLabelSetValue(faceLabel, nStart + numVertices + numEdges + bodyFaceIndexStart + fID - 1, fID));
1107     }
1108     EG_free(fobjs);
1109   }
1110 
1111   PetscCall(PetscHMapIDestroy(&edgeMap));
1112   PetscCall(PetscHMapIDestroy(&bodyIndexMap));
1113   PetscCall(PetscHMapIDestroy(&bodyVertexMap));
1114   PetscCall(PetscHMapIDestroy(&bodyEdgeMap));
1115   PetscCall(PetscHMapIDestroy(&bodyEdgeGlobalMap));
1116   PetscCall(PetscHMapIDestroy(&bodyFaceMap));
1117 
1118   *newdm = dm;
1119   PetscFunctionReturn(PETSC_SUCCESS);
1120 }
1121 
1122 static PetscErrorCode DMPlexCreateEGADS_Tess_Internal(MPI_Comm comm, ego context, ego model, DM *newdm)
1123 {
1124   DMLabel bodyLabel, faceLabel, edgeLabel, vertexLabel;
1125   /* EGADSLite variables */
1126   ego    geom, *bodies, *fobjs;
1127   int    b, oclass, mtype, nbodies, *senses;
1128   int    totalNumTris = 0, totalNumPoints = 0;
1129   double boundBox[6] = {0., 0., 0., 0., 0., 0.}, tessSize;
1130   /* PETSc variables */
1131   DM              dm;
1132   PetscHMapI      pointIndexStartMap = NULL, triIndexStartMap = NULL, pTypeLabelMap = NULL, pIndexLabelMap = NULL;
1133   PetscHMapI      pBodyIndexLabelMap = NULL, triFaceIDLabelMap = NULL, triBodyIDLabelMap = NULL;
1134   PetscInt        dim = -1, cdim = -1, numCorners = 0, counter = 0;
1135   PetscInt       *cells  = NULL;
1136   const PetscInt *cone   = NULL;
1137   PetscReal      *coords = NULL;
1138   PetscMPIInt     rank;
1139 
1140   PetscFunctionBeginUser;
1141   PetscCallMPI(MPI_Comm_rank(comm, &rank));
1142   if (rank == 0) {
1143     // ---------------------------------------------------------------------------------------------------
1144     // Generate Petsc Plex from EGADSlite created Tessellation of geometry
1145     // ---------------------------------------------------------------------------------------------------
1146 
1147     // Calculate cell and vertex sizes
1148     PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &nbodies, &bodies, &senses));
1149 
1150     PetscCall(PetscHMapICreate(&pointIndexStartMap));
1151     PetscCall(PetscHMapICreate(&triIndexStartMap));
1152     PetscCall(PetscHMapICreate(&pTypeLabelMap));
1153     PetscCall(PetscHMapICreate(&pIndexLabelMap));
1154     PetscCall(PetscHMapICreate(&pBodyIndexLabelMap));
1155     PetscCall(PetscHMapICreate(&triFaceIDLabelMap));
1156     PetscCall(PetscHMapICreate(&triBodyIDLabelMap));
1157 
1158     /* Create Tessellation of Bodies */
1159     ego tessArray[nbodies];
1160 
1161     for (b = 0; b < nbodies; ++b) {
1162       ego           body      = bodies[b];
1163       double        params[3] = {0.0, 0.0, 0.0}; // Parameters for Tessellation
1164       int           Nf, bodyNumPoints = 0, bodyNumTris = 0;
1165       PetscHashIter PISiter, TISiter;
1166       PetscBool     PISfound, TISfound;
1167 
1168       /* Store Start Index for each Body's Point and Tris */
1169       PetscCall(PetscHMapIFind(pointIndexStartMap, b, &PISiter, &PISfound));
1170       PetscCall(PetscHMapIFind(triIndexStartMap, b, &TISiter, &TISfound));
1171 
1172       if (!PISfound) PetscCall(PetscHMapISet(pointIndexStartMap, b, totalNumPoints));
1173       if (!TISfound) PetscCall(PetscHMapISet(triIndexStartMap, b, totalNumTris));
1174 
1175       /* Calculate Tessellation parameters based on Bounding Box */
1176       /* Get Bounding Box Dimensions of the BODY */
1177       PetscCall(EG_getBoundingBox(body, boundBox));
1178       tessSize = boundBox[3] - boundBox[0];
1179       if (tessSize < boundBox[4] - boundBox[1]) tessSize = boundBox[4] - boundBox[1];
1180       if (tessSize < boundBox[5] - boundBox[2]) tessSize = boundBox[5] - boundBox[2];
1181 
1182       // TODO :: May want to give users tessellation parameter options //
1183       params[0] = 0.0250 * tessSize;
1184       params[1] = 0.0075 * tessSize;
1185       params[2] = 15.0;
1186 
1187       PetscCall(EG_makeTessBody(body, params, &tessArray[b]));
1188 
1189       PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
1190 
1191       for (int f = 0; f < Nf; ++f) {
1192         ego           face = fobjs[f];
1193         int           len, fID, ntris;
1194         const int    *ptype, *pindex, *ptris, *ptric;
1195         const double *pxyz, *puv;
1196 
1197         // Get Face ID //
1198         fID = EG_indexBodyTopo(body, face);
1199 
1200         // Checkout the Surface Tessellation //
1201         PetscCall(EG_getTessFace(tessArray[b], fID, &len, &pxyz, &puv, &ptype, &pindex, &ntris, &ptris, &ptric));
1202 
1203         // Determine total number of triangle cells in the tessellation //
1204         bodyNumTris += (int)ntris;
1205 
1206         // Check out the point index and coordinate //
1207         for (int p = 0; p < len; ++p) {
1208           int global;
1209 
1210           PetscCall(EG_localToGlobal(tessArray[b], fID, p + 1, &global));
1211 
1212           // Determine the total number of points in the tessellation //
1213           bodyNumPoints = PetscMax(bodyNumPoints, global);
1214         }
1215       }
1216       EG_free(fobjs);
1217 
1218       totalNumPoints += bodyNumPoints;
1219       totalNumTris += bodyNumTris;
1220     }
1221     //}  - Original End of (rank == 0)
1222 
1223     dim        = 2;
1224     cdim       = 3;
1225     numCorners = 3;
1226     //PetscInt counter = 0;
1227 
1228     /* NEED TO DEFINE MATRICES/VECTORS TO STORE GEOM REFERENCE DATA   */
1229     /* Fill in below and use to define DMLabels after DMPlex creation */
1230     PetscCall(PetscMalloc2(totalNumPoints * cdim, &coords, totalNumTris * numCorners, &cells));
1231 
1232     for (b = 0; b < nbodies; ++b) {
1233       ego           body = bodies[b];
1234       int           Nf;
1235       PetscInt      pointIndexStart;
1236       PetscHashIter PISiter;
1237       PetscBool     PISfound;
1238 
1239       PetscCall(PetscHMapIFind(pointIndexStartMap, b, &PISiter, &PISfound));
1240       PetscCheck(PISfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in pointIndexStartMap", b);
1241       PetscCall(PetscHMapIGet(pointIndexStartMap, b, &pointIndexStart));
1242 
1243       PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
1244 
1245       for (int f = 0; f < Nf; ++f) {
1246         /* Get Face Object */
1247         ego           face = fobjs[f];
1248         int           len, fID, ntris;
1249         const int    *ptype, *pindex, *ptris, *ptric;
1250         const double *pxyz, *puv;
1251 
1252         /* Get Face ID */
1253         fID = EG_indexBodyTopo(body, face);
1254 
1255         /* Checkout the Surface Tessellation */
1256         PetscCall(EG_getTessFace(tessArray[b], fID, &len, &pxyz, &puv, &ptype, &pindex, &ntris, &ptris, &ptric));
1257 
1258         /* Check out the point index and coordinate */
1259         for (int p = 0; p < len; ++p) {
1260           int           global;
1261           PetscHashIter PTLiter, PILiter, PBLiter;
1262           PetscBool     PTLfound, PILfound, PBLfound;
1263 
1264           PetscCall(EG_localToGlobal(tessArray[b], fID, p + 1, &global));
1265 
1266           /* Set the coordinates array for DAG */
1267           coords[((global - 1 + pointIndexStart) * 3) + 0] = pxyz[(p * 3) + 0];
1268           coords[((global - 1 + pointIndexStart) * 3) + 1] = pxyz[(p * 3) + 1];
1269           coords[((global - 1 + pointIndexStart) * 3) + 2] = pxyz[(p * 3) + 2];
1270 
1271           /* Store Geometry Label Information for DMLabel assignment later */
1272           PetscCall(PetscHMapIFind(pTypeLabelMap, global - 1 + pointIndexStart, &PTLiter, &PTLfound));
1273           PetscCall(PetscHMapIFind(pIndexLabelMap, global - 1 + pointIndexStart, &PILiter, &PILfound));
1274           PetscCall(PetscHMapIFind(pBodyIndexLabelMap, global - 1 + pointIndexStart, &PBLiter, &PBLfound));
1275 
1276           if (!PTLfound) PetscCall(PetscHMapISet(pTypeLabelMap, global - 1 + pointIndexStart, ptype[p]));
1277           if (!PILfound) PetscCall(PetscHMapISet(pIndexLabelMap, global - 1 + pointIndexStart, pindex[p]));
1278           if (!PBLfound) PetscCall(PetscHMapISet(pBodyIndexLabelMap, global - 1 + pointIndexStart, b));
1279 
1280           if (ptype[p] < 0) PetscCall(PetscHMapISet(pIndexLabelMap, global - 1 + pointIndexStart, fID));
1281         }
1282 
1283         for (int t = 0; t < (int)ntris; ++t) {
1284           int           global, globalA, globalB;
1285           PetscHashIter TFLiter, TBLiter;
1286           PetscBool     TFLfound, TBLfound;
1287 
1288           PetscCall(EG_localToGlobal(tessArray[b], fID, ptris[(t * 3) + 0], &global));
1289           cells[(counter * 3) + 0] = global - 1 + pointIndexStart;
1290 
1291           PetscCall(EG_localToGlobal(tessArray[b], fID, ptris[(t * 3) + 1], &globalA));
1292           cells[(counter * 3) + 1] = globalA - 1 + pointIndexStart;
1293 
1294           PetscCall(EG_localToGlobal(tessArray[b], fID, ptris[(t * 3) + 2], &globalB));
1295           cells[(counter * 3) + 2] = globalB - 1 + pointIndexStart;
1296 
1297           PetscCall(PetscHMapIFind(triFaceIDLabelMap, counter, &TFLiter, &TFLfound));
1298           PetscCall(PetscHMapIFind(triBodyIDLabelMap, counter, &TBLiter, &TBLfound));
1299 
1300           if (!TFLfound) PetscCall(PetscHMapISet(triFaceIDLabelMap, counter, fID));
1301           if (!TBLfound) PetscCall(PetscHMapISet(triBodyIDLabelMap, counter, b));
1302 
1303           counter += 1;
1304         }
1305       }
1306       EG_free(fobjs);
1307     }
1308   }
1309 
1310   //Build DMPlex
1311   PetscCall(DMPlexCreateFromCellListPetsc(PETSC_COMM_WORLD, dim, totalNumTris, totalNumPoints, numCorners, PETSC_TRUE, cells, cdim, coords, &dm));
1312   PetscCall(PetscFree2(coords, cells));
1313 
1314   // Embed EGADS model in DM
1315   {
1316     PetscContainer modelObj, contextObj;
1317 
1318     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &modelObj));
1319     PetscCall(PetscContainerSetPointer(modelObj, model));
1320     PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Model", (PetscObject)modelObj));
1321     PetscCall(PetscContainerDestroy(&modelObj));
1322 
1323     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &contextObj));
1324     PetscCall(PetscContainerSetPointer(contextObj, context));
1325     PetscCall(PetscContainerSetUserDestroy(contextObj, DMPlexEGADSDestroy_Private));
1326     PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Context", (PetscObject)contextObj));
1327     PetscCall(PetscContainerDestroy(&contextObj));
1328   }
1329 
1330   // Label Points
1331   PetscCall(DMCreateLabel(dm, "EGADS Body ID"));
1332   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
1333   PetscCall(DMCreateLabel(dm, "EGADS Face ID"));
1334   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
1335   PetscCall(DMCreateLabel(dm, "EGADS Edge ID"));
1336   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
1337   PetscCall(DMCreateLabel(dm, "EGADS Vertex ID"));
1338   PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));
1339 
1340   /* Get Number of DAG Nodes at each level */
1341   int fStart, fEnd, eStart, eEnd, nStart, nEnd;
1342 
1343   PetscCall(DMPlexGetHeightStratum(dm, 0, &fStart, &fEnd));
1344   PetscCall(DMPlexGetHeightStratum(dm, 1, &eStart, &eEnd));
1345   PetscCall(DMPlexGetHeightStratum(dm, 2, &nStart, &nEnd));
1346 
1347   /* Set DMLabels for NODES */
1348   for (int n = nStart; n < nEnd; ++n) {
1349     int           pTypeVal, pIndexVal, pBodyVal;
1350     PetscHashIter PTLiter, PILiter, PBLiter;
1351     PetscBool     PTLfound, PILfound, PBLfound;
1352 
1353     //Converted to Hash Tables
1354     PetscCall(PetscHMapIFind(pTypeLabelMap, n - nStart, &PTLiter, &PTLfound));
1355     PetscCheck(PTLfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "DAG Point %d not found in pTypeLabelMap", n);
1356     PetscCall(PetscHMapIGet(pTypeLabelMap, n - nStart, &pTypeVal));
1357 
1358     PetscCall(PetscHMapIFind(pIndexLabelMap, n - nStart, &PILiter, &PILfound));
1359     PetscCheck(PILfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "DAG Point %d not found in pIndexLabelMap", n);
1360     PetscCall(PetscHMapIGet(pIndexLabelMap, n - nStart, &pIndexVal));
1361 
1362     PetscCall(PetscHMapIFind(pBodyIndexLabelMap, n - nStart, &PBLiter, &PBLfound));
1363     PetscCheck(PBLfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "DAG Point %d not found in pBodyLabelMap", n);
1364     PetscCall(PetscHMapIGet(pBodyIndexLabelMap, n - nStart, &pBodyVal));
1365 
1366     PetscCall(DMLabelSetValue(bodyLabel, n, pBodyVal));
1367     if (pTypeVal == 0) PetscCall(DMLabelSetValue(vertexLabel, n, pIndexVal));
1368     if (pTypeVal > 0) PetscCall(DMLabelSetValue(edgeLabel, n, pIndexVal));
1369     if (pTypeVal < 0) PetscCall(DMLabelSetValue(faceLabel, n, pIndexVal));
1370   }
1371 
1372   /* Set DMLabels for Edges - Based on the DMLabels of the EDGE's NODES */
1373   for (int e = eStart; e < eEnd; ++e) {
1374     int bodyID_0, vertexID_0, vertexID_1, edgeID_0, edgeID_1, faceID_0, faceID_1;
1375 
1376     PetscCall(DMPlexGetCone(dm, e, &cone));
1377     PetscCall(DMLabelGetValue(bodyLabel, cone[0], &bodyID_0)); // Do I need to check the other end?
1378     PetscCall(DMLabelGetValue(vertexLabel, cone[0], &vertexID_0));
1379     PetscCall(DMLabelGetValue(vertexLabel, cone[1], &vertexID_1));
1380     PetscCall(DMLabelGetValue(edgeLabel, cone[0], &edgeID_0));
1381     PetscCall(DMLabelGetValue(edgeLabel, cone[1], &edgeID_1));
1382     PetscCall(DMLabelGetValue(faceLabel, cone[0], &faceID_0));
1383     PetscCall(DMLabelGetValue(faceLabel, cone[1], &faceID_1));
1384 
1385     PetscCall(DMLabelSetValue(bodyLabel, e, bodyID_0));
1386 
1387     if (edgeID_0 == edgeID_1) PetscCall(DMLabelSetValue(edgeLabel, e, edgeID_0));
1388     else if (vertexID_0 > 0 && edgeID_1 > 0) PetscCall(DMLabelSetValue(edgeLabel, e, edgeID_1));
1389     else if (vertexID_1 > 0 && edgeID_0 > 0) PetscCall(DMLabelSetValue(edgeLabel, e, edgeID_0));
1390     else { /* Do Nothing */ }
1391   }
1392 
1393   /* Set DMLabels for Cells */
1394   for (int f = fStart; f < fEnd; ++f) {
1395     int           edgeID_0;
1396     PetscInt      triBodyVal, triFaceVal;
1397     PetscHashIter TFLiter, TBLiter;
1398     PetscBool     TFLfound, TBLfound;
1399 
1400     // Convert to Hash Table
1401     PetscCall(PetscHMapIFind(triFaceIDLabelMap, f - fStart, &TFLiter, &TFLfound));
1402     PetscCheck(TFLfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "DAG Point %d not found in triFaceIDLabelMap", f);
1403     PetscCall(PetscHMapIGet(triFaceIDLabelMap, f - fStart, &triFaceVal));
1404 
1405     PetscCall(PetscHMapIFind(triBodyIDLabelMap, f - fStart, &TBLiter, &TBLfound));
1406     PetscCheck(TBLfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "DAG Point %d not found in triBodyIDLabelMap", f);
1407     PetscCall(PetscHMapIGet(triBodyIDLabelMap, f - fStart, &triBodyVal));
1408 
1409     PetscCall(DMLabelSetValue(bodyLabel, f, triBodyVal));
1410     PetscCall(DMLabelSetValue(faceLabel, f, triFaceVal));
1411 
1412     /* Finish Labeling previously unlabeled DMPlex Edges - Assumes Triangular Cell (3 Edges Max) */
1413     PetscCall(DMPlexGetCone(dm, f, &cone));
1414 
1415     for (int jj = 0; jj < 3; ++jj) {
1416       PetscCall(DMLabelGetValue(edgeLabel, cone[jj], &edgeID_0));
1417 
1418       if (edgeID_0 < 0) {
1419         PetscCall(DMLabelSetValue(bodyLabel, cone[jj], triBodyVal));
1420         PetscCall(DMLabelSetValue(faceLabel, cone[jj], triFaceVal));
1421       }
1422     }
1423   }
1424 
1425   *newdm = dm;
1426   PetscFunctionReturn(PETSC_SUCCESS);
1427 }
1428 #endif
1429 
1430 /*@
1431   DMPlexInflateToGeomModel - Snaps the vertex coordinates of a `DMPLEX` object representing the mesh to its geometry if some vertices depart from the model.
1432   This usually happens with non-conforming refinement.
1433 
1434   Collective
1435 
1436   Input Parameter:
1437 . dm - The uninflated `DM` object representing the mesh
1438 
1439   Level: intermediate
1440 
1441 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexCreateEGADS()`
1442 @*/
1443 PetscErrorCode DMPlexInflateToGeomModel(DM dm)
1444 {
1445 #if defined(PETSC_HAVE_EGADS)
1446   /* EGADS Variables */
1447   ego    model, geom, body, face, edge;
1448   ego   *bodies;
1449   int    Nb, oclass, mtype, *senses;
1450   double result[3];
1451   /* PETSc Variables */
1452   DM             cdm;
1453   PetscContainer modelObj;
1454   DMLabel        bodyLabel, faceLabel, edgeLabel, vertexLabel;
1455   Vec            coordinates;
1456   PetscScalar   *coords;
1457   PetscInt       bodyID, faceID, edgeID, vertexID;
1458   PetscInt       cdim, d, vStart, vEnd, v;
1459 #endif
1460 
1461   PetscFunctionBegin;
1462 #if defined(PETSC_HAVE_EGADS)
1463   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
1464   if (!modelObj) PetscFunctionReturn(PETSC_SUCCESS);
1465   PetscCall(DMGetCoordinateDim(dm, &cdim));
1466   PetscCall(DMGetCoordinateDM(dm, &cdm));
1467   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1468   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
1469   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
1470   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
1471   PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));
1472 
1473   PetscCall(PetscContainerGetPointer(modelObj, (void **)&model));
1474   PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
1475 
1476   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1477   PetscCall(VecGetArrayWrite(coordinates, &coords));
1478   for (v = vStart; v < vEnd; ++v) {
1479     PetscScalar *vcoords;
1480 
1481     PetscCall(DMLabelGetValue(bodyLabel, v, &bodyID));
1482     PetscCall(DMLabelGetValue(faceLabel, v, &faceID));
1483     PetscCall(DMLabelGetValue(edgeLabel, v, &edgeID));
1484     PetscCall(DMLabelGetValue(vertexLabel, v, &vertexID));
1485 
1486     PetscCheck(bodyID < Nb, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Body %" PetscInt_FMT " is not in [0, %d)", bodyID, Nb);
1487     body = bodies[bodyID];
1488 
1489     PetscCall(DMPlexPointLocalRef(cdm, v, coords, (void *)&vcoords));
1490     if (edgeID > 0) {
1491       /* Snap to EDGE at nearest location */
1492       double params[1];
1493       PetscCall(EG_objectBodyTopo(body, EDGE, edgeID, &edge));
1494       PetscCall(EG_invEvaluate(edge, vcoords, params, result)); // Get (x,y,z) of nearest point on EDGE
1495       for (d = 0; d < cdim; ++d) vcoords[d] = result[d];
1496     } else if (faceID > 0) {
1497       /* Snap to FACE at nearest location */
1498       double params[2];
1499       PetscCall(EG_objectBodyTopo(body, FACE, faceID, &face));
1500       PetscCall(EG_invEvaluate(face, vcoords, params, result)); // Get (x,y,z) of nearest point on FACE
1501       for (d = 0; d < cdim; ++d) vcoords[d] = result[d];
1502     }
1503   }
1504   PetscCall(VecRestoreArrayWrite(coordinates, &coords));
1505   /* Clear out global coordinates */
1506   PetscCall(VecDestroy(&dm->coordinates[0].x));
1507 #endif
1508   PetscFunctionReturn(PETSC_SUCCESS);
1509 }
1510 
1511 /*@C
1512   DMPlexCreateEGADSFromFile - Create a `DMPLEX` mesh from an EGADS, IGES, or STEP file.
1513 
1514   Collective
1515 
1516   Input Parameters:
1517 + comm     - The MPI communicator
1518 - filename - The name of the EGADS, IGES, or STEP file
1519 
1520   Output Parameter:
1521 . dm - The `DM` object representing the mesh
1522 
1523   Level: beginner
1524 
1525 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexCreateEGADS()`, `DMPlexCreateEGADSLiteFromFile()`
1526 @*/
1527 PetscErrorCode DMPlexCreateEGADSFromFile(MPI_Comm comm, const char filename[], DM *dm)
1528 {
1529   PetscMPIInt rank;
1530 #if defined(PETSC_HAVE_EGADS)
1531   ego context = NULL, model = NULL;
1532 #endif
1533   PetscBool printModel = PETSC_FALSE, tessModel = PETSC_FALSE, newModel = PETSC_FALSE;
1534 
1535   PetscFunctionBegin;
1536   PetscAssertPointer(filename, 2);
1537   PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_egads_print_model", &printModel, NULL));
1538   PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_egads_tess_model", &tessModel, NULL));
1539   PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_egads_new_model", &newModel, NULL));
1540   PetscCallMPI(MPI_Comm_rank(comm, &rank));
1541 #if defined(PETSC_HAVE_EGADS)
1542   if (rank == 0) {
1543     PetscCall(EG_open(&context));
1544     PetscCall(EG_loadModel(context, 0, filename, &model));
1545     if (printModel) PetscCall(DMPlexEGADSPrintModel_Internal(model));
1546   }
1547   if (tessModel) PetscCall(DMPlexCreateEGADS_Tess_Internal(comm, context, model, dm));
1548   else if (newModel) PetscCall(DMPlexCreateEGADS(comm, context, model, dm));
1549   else PetscCall(DMPlexCreateEGADS_Internal(comm, context, model, dm));
1550   PetscFunctionReturn(PETSC_SUCCESS);
1551 #else
1552   SETERRQ(comm, PETSC_ERR_SUP, "This method requires EGADS support. Reconfigure using --download-egads");
1553 #endif
1554 }
1555