xref: /petsc/src/dm/impls/plex/plexegads.c (revision 0baf8eba40dbc839082666f9f7396a225d6f663c) !
1 #include "petscsys.h"
2 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
3 #include <petsc/private/hashmapi.h>
4 
5 /* We need to understand how to natively parse STEP files. There seems to be only one open-source implementation of
6    the STEP parser contained in the OpenCASCADE package. It is enough to make a strong man weep:
7 
8      https://github.com/tpaviot/oce/tree/master/src/STEPControl
9 
10    The STEP, and inner EXPRESS, formats are ISO standards, so they are documented
11 
12      https://stackoverflow.com/questions/26774037/documentation-or-specification-for-step-and-stp-files
13      http://stepmod.sourceforge.net/express_model_spec/
14 
15    but again it seems that there has been a deliberate effort at obfuscation, probably to raise the bar for entrants.
16 */
17 
18 #ifdef PETSC_HAVE_EGADS
19   #include <petscdmplexegads.h>
20 
21 PETSC_INTERN PetscErrorCode DMSnapToGeomModel_EGADS_Internal(DM, PetscInt, ego, PetscInt, PetscInt, PetscInt, const PetscScalar[], PetscScalar[], PetscBool);
22 PETSC_INTERN PetscErrorCode DMPlex_Geom_EDGE_XYZtoUV_Internal(const PetscScalar[], ego, const PetscScalar[], const PetscInt, const PetscInt, PetscScalar[], PetscBool);
23 PETSC_INTERN PetscErrorCode DMPlex_Geom_FACE_XYZtoUV_Internal(const PetscScalar[], ego, const PetscScalar[], const PetscInt, const PetscInt, PetscScalar[], PetscBool);
24 
25 PetscErrorCode DMPlex_EGADS_GeomDecode_Internal(const PetscInt geomClass, const PetscInt geomType, char **retClass, char **retType)
26 {
27   PetscFunctionBeginHot;
28   /* EGADS Object Type */
29   if (geomClass == CONTXT) { *retClass = (char *)"CONTEXT"; }
30   if (geomClass == TRANSFORM) { *retClass = (char *)"TRANSFORM"; }
31   if (geomClass == TESSELLATION) { *retClass = (char *)"TESSELLATION"; }
32   if (geomClass == NIL) { *retClass = (char *)"NIL"; }
33   if (geomClass == EMPTY) { *retClass = (char *)"EMPTY"; }
34   if (geomClass == REFERENCE) { *retClass = (char *)"REFERENCE"; }
35   if (geomClass == PCURVE) { *retClass = (char *)"PCURVE"; }
36   if (geomClass == CURVE) { *retClass = (char *)"CURVE"; }
37   if (geomClass == SURFACE) { *retClass = (char *)"SURFACE"; }
38   if (geomClass == NODE) { *retClass = (char *)"NODE"; }
39   if (geomClass == EDGE) { *retClass = (char *)"EDGE"; }
40   if (geomClass == LOOP) { *retClass = (char *)"LOOP"; }
41   if (geomClass == FACE) { *retClass = (char *)"FACE"; }
42   if (geomClass == SHELL) { *retClass = (char *)"SHELL"; }
43   if (geomClass == BODY) { *retClass = (char *)"BODY"; }
44   if (geomClass == MODEL) { *retClass = (char *)"MODEL"; }
45 
46   /* PCURVES & CURVES */
47   if (geomClass == PCURVE || geomClass == CURVE) {
48     if (geomType == LINE) { *retType = (char *)"LINE"; }
49     if (geomType == CIRCLE) { *retType = (char *)"CIRCLE"; }
50     if (geomType == ELLIPSE) { *retType = (char *)"ELLIPSE"; }
51     if (geomType == PARABOLA) { *retType = (char *)"PARABOLA"; }
52     if (geomType == HYPERBOLA) { *retType = (char *)"HYPERBOLA"; }
53     if (geomType == TRIMMED) { *retType = (char *)"TRIMMED"; }
54     if (geomType == BEZIER) { *retType = (char *)"BEZIER"; }
55     if (geomType == BSPLINE) { *retType = (char *)"BSPLINE"; }
56     if (geomType == OFFSET) { *retType = (char *)"OFFSET"; }
57   }
58 
59   /* SURFACE */
60   if (geomClass == SURFACE) {
61     if (geomType == PLANE) { *retType = (char *)"PLANE"; }
62     if (geomType == SPHERICAL) { *retType = (char *)"SPHERICAL"; }
63     if (geomType == CYLINDRICAL) { *retType = (char *)"CYLINDRICAL"; }
64     if (geomType == REVOLUTION) { *retType = (char *)"REVOLUTION"; }
65     if (geomType == TOROIDAL) { *retType = (char *)"TOROIDAL"; }
66     if (geomType == CONICAL) { *retType = (char *)"CONICAL"; }
67     if (geomType == EXTRUSION) { *retType = (char *)"EXTRUSION"; }
68     if (geomType == BEZIER) { *retType = (char *)"BEZIER"; }
69     if (geomType == BSPLINE) { *retType = (char *)"BSPLINE"; }
70   }
71 
72   /* TOPOLOGY */
73   if (geomClass == NODE || geomClass == EDGE || geomClass == LOOP || geomClass == FACE || geomClass == SHELL || geomClass == BODY || geomClass == MODEL) {
74     if (geomType == SREVERSE) { *retType = (char *)"SREVERSE"; }
75     if (geomType == NOMTYPE) { *retType = (char *)"NOMTYPE"; }
76     if (geomType == SFORWARD && geomClass == FACE) { *retType = (char *)"SFORWARD"; }
77     if (geomType == ONENODE && geomClass == EDGE) { *retType = (char *)"ONENODE"; }
78     if (geomType == TWONODE) { *retType = (char *)"TWONODE"; }
79     if (geomType == OPEN) { *retType = (char *)"OPEN"; }
80     if (geomType == CLOSED) { *retType = (char *)"CLOSED"; }
81     if (geomType == DEGENERATE) { *retType = (char *)"DEGENERATE"; }
82     if (geomType == WIREBODY) { *retType = (char *)"WIREBODY"; }
83     if (geomType == FACEBODY) { *retType = (char *)"FACEBODY"; }
84     if (geomType == SHEETBODY) { *retType = (char *)"SHEETBODY"; }
85     if (geomType == SOLIDBODY) { *retType = (char *)"SOLIDBODY"; }
86   }
87   PetscFunctionReturn(PETSC_SUCCESS);
88 }
89 
90 PetscErrorCode DMPlex_EGADS_EDGE_XYZtoUV_Internal(const PetscScalar coords[], ego obj, const PetscScalar range[], const PetscInt v, const PetscInt dE, PetscScalar paramsV[])
91 {
92   //
93   //
94   // Depreciated. Changed all references to DMPlex_Geom_FACE_XYZtoUV_Internal()
95   //
96   //
97 
98   PetscInt    loopCntr = 0;
99   PetscScalar dx, dy, dz, lambda, tolr, obj_old, obj_tmp, target;
100   PetscScalar delta, A, b;
101   PetscScalar ts[2], tt[2], eval[18], data[18];
102 
103   PetscFunctionBeginHot;
104   /* Initialize Levenberg-Marquardt parameters */
105   lambda = 1.0;
106   tolr   = 1.0;
107   target = 1.0E-20;
108   ts[0]  = (range[0] + range[1]) / 2.;
109 
110   while (tolr >= target) {
111     PetscCall(EG_evaluate(obj, ts, eval));
112     dx      = coords[v * dE + 0] - eval[0];
113     dy      = coords[v * dE + 1] - eval[1];
114     dz      = coords[v * dE + 2] - eval[2];
115     obj_old = dx * dx + dy * dy + dz * dz;
116 
117     if (obj_old < target) {
118       tolr = obj_old;
119       break;
120     }
121 
122     A = (eval[3] * eval[3] + eval[4] * eval[4] + eval[5] * eval[5]) * (1.0 + lambda);
123     if (A == 0.0) {
124       PetscCall(PetscPrintf(PETSC_COMM_SELF, "A = 0.0 \n"));
125       break;
126     }
127     b = eval[3] * dx + eval[4] * dy + eval[5] * dz;
128 
129     /* Solve A*delta = b */
130     delta = b / A;
131 
132     /* Find a temp (u,v) and associated objective function */
133     tt[0] = ts[0] + delta;
134     if (tt[0] < range[0]) {
135       tt[0] = range[0];
136       delta = tt[0] - ts[0];
137     }
138     if (tt[0] > range[1]) {
139       tt[0] = range[1];
140       delta = tt[0] - ts[0];
141     }
142 
143     PetscCall(EG_evaluate(obj, tt, data));
144 
145     obj_tmp = (coords[v * dE + 0] - data[0]) * (coords[v * dE + 0] - data[0]) + (coords[v * dE + 1] - data[1]) * (coords[v * dE + 1] - data[1]) + (coords[v * dE + 2] - data[2]) * (coords[v * dE + 2] - data[2]);
146 
147     /* If step is better, accept it and halve lambda (making it more Newton-like) */
148     if (obj_tmp < obj_old) {
149       obj_old = obj_tmp;
150       ts[0]   = tt[0];
151       for (int jj = 0; jj < 18; ++jj) eval[jj] = data[jj];
152       lambda /= 2.0;
153       if (lambda < 1.0E-14) lambda = 1.0E-14;
154       if (obj_old < target) {
155         tolr = obj_old;
156         break;
157       }
158     } else {
159       /* Otherwise reject it and double lambda (making it more gradient-descent like) */
160       lambda *= 2.0;
161     }
162 
163     if ((tt[0] == range[0]) || (tt[0] == range[1])) break;
164     if (fabs(delta) < target) {
165       tolr = obj_old;
166       break;
167     }
168 
169     tolr = obj_old;
170 
171     loopCntr += 1;
172     if (loopCntr > 100) break;
173   }
174   paramsV[v * 3 + 0] = ts[0];
175   paramsV[v * 3 + 1] = 0.;
176   PetscFunctionReturn(PETSC_SUCCESS);
177 }
178 
179 PetscErrorCode DMPlex_Geom_EDGE_XYZtoUV_Internal(const PetscScalar coords[], ego obj, const PetscScalar range[], const PetscInt v, const PetscInt dE, PetscScalar paramsV[], PetscBool islite)
180 {
181   PetscInt    loopCntr = 0;
182   PetscScalar dx, dy, dz, lambda, tolr, obj_old, obj_tmp, target;
183   PetscScalar delta, A, b;
184   PetscScalar ts[2], tt[2], eval[18], data[18];
185 
186   PetscFunctionBeginHot;
187   /* Initialize Levenberg-Marquardt parameters */
188   lambda = 1.0;
189   tolr   = 1.0;
190   target = 1.0E-20;
191   ts[0]  = (range[0] + range[1]) / 2.;
192 
193   while (tolr >= target) {
194     if (islite) {
195       PetscCall(EGlite_evaluate(obj, ts, eval));
196     } else {
197       PetscCall(EG_evaluate(obj, ts, eval));
198     }
199 
200     dx      = coords[v * dE + 0] - eval[0];
201     dy      = coords[v * dE + 1] - eval[1];
202     dz      = coords[v * dE + 2] - eval[2];
203     obj_old = dx * dx + dy * dy + dz * dz;
204 
205     if (obj_old < target) {
206       tolr = obj_old;
207       break;
208     }
209 
210     A = (eval[3] * eval[3] + eval[4] * eval[4] + eval[5] * eval[5]) * (1.0 + lambda);
211     if (A == 0.0) {
212       PetscCall(PetscPrintf(PETSC_COMM_SELF, "A = 0.0 \n"));
213       break;
214     }
215     b = eval[3] * dx + eval[4] * dy + eval[5] * dz;
216 
217     /* Solve A*delta = b */
218     delta = b / A;
219 
220     /* Find a temp (u,v) and associated objective function */
221     tt[0] = ts[0] + delta;
222     if (tt[0] < range[0]) {
223       tt[0] = range[0];
224       delta = tt[0] - ts[0];
225     }
226     if (tt[0] > range[1]) {
227       tt[0] = range[1];
228       delta = tt[0] - ts[0];
229     }
230 
231     if (islite) {
232       PetscCall(EGlite_evaluate(obj, tt, data));
233     } else {
234       PetscCall(EG_evaluate(obj, tt, data));
235     }
236 
237     obj_tmp = (coords[v * dE + 0] - data[0]) * (coords[v * dE + 0] - data[0]) + (coords[v * dE + 1] - data[1]) * (coords[v * dE + 1] - data[1]) + (coords[v * dE + 2] - data[2]) * (coords[v * dE + 2] - data[2]);
238 
239     /* If step is better, accept it and halve lambda (making it more Newton-like) */
240     if (obj_tmp < obj_old) {
241       obj_old = obj_tmp;
242       ts[0]   = tt[0];
243       for (int jj = 0; jj < 18; ++jj) eval[jj] = data[jj];
244       lambda /= 2.0;
245       if (lambda < 1.0E-14) lambda = 1.0E-14;
246       if (obj_old < target) {
247         tolr = obj_old;
248         break;
249       }
250     } else {
251       /* Otherwise reject it and double lambda (making it more gradient-descent like) */
252       lambda *= 2.0;
253     }
254 
255     if ((tt[0] == range[0]) || (tt[0] == range[1])) break;
256     if (fabs(delta) < target) {
257       tolr = obj_old;
258       break;
259     }
260 
261     tolr = obj_old;
262 
263     loopCntr += 1;
264     if (loopCntr > 100) break;
265   }
266   paramsV[v * 3 + 0] = ts[0];
267   paramsV[v * 3 + 1] = 0.;
268   PetscFunctionReturn(PETSC_SUCCESS);
269 }
270 
271 PetscErrorCode DMPlex_EGADS_FACE_XYZtoUV_Internal(const PetscScalar coords[], ego obj, const PetscScalar range[], const PetscInt v, const PetscInt dE, PetscScalar paramsV[])
272 {
273   //
274   //
275   // Depreciated. Changed all references to DMPlex_Geom_FACE_XYZtoUV_Internal()
276   //
277   //
278 
279   PetscInt    loopCntr = 0;
280   PetscScalar dx, dy, dz, lambda, tolr, denom, obj_old, obj_tmp, target;
281   PetscScalar uvs[2], uvt[2], delta[2], A[4], b[2], eval[18], data[18];
282 
283   PetscFunctionBeginHot;
284   /* Initialize Levenberg-Marquardt parameters */
285   lambda = 1.0;
286   tolr   = 1.0;
287   target = 1.0E-20;
288   uvs[0] = (range[0] + range[1]) / 2.;
289   uvs[1] = (range[2] + range[3]) / 2.;
290 
291   while (tolr >= target) {
292     PetscCall(EG_evaluate(obj, uvs, eval));
293     dx      = coords[v * dE + 0] - eval[0];
294     dy      = coords[v * dE + 1] - eval[1];
295     dz      = coords[v * dE + 2] - eval[2];
296     obj_old = dx * dx + dy * dy + dz * dz;
297 
298     if (obj_old < target) {
299       tolr = obj_old;
300       break;
301     }
302 
303     A[0] = (eval[3] * eval[3] + eval[4] * eval[4] + eval[5] * eval[5]) * (1.0 + lambda);
304     A[1] = eval[3] * eval[6] + eval[4] * eval[7] + eval[5] * eval[8];
305     A[2] = A[1];
306     A[3] = (eval[6] * eval[6] + eval[7] * eval[7] + eval[8] * eval[8]) * (1.0 + lambda);
307 
308     b[0] = eval[3] * dx + eval[4] * dy + eval[5] * dz;
309     b[1] = eval[6] * dx + eval[7] * dy + eval[8] * dz;
310 
311     /* Solve A*delta = b using Cramer's Rule */
312     denom = A[0] * A[3] - A[2] * A[1];
313     if (denom == 0.0) { PetscCall(PetscPrintf(PETSC_COMM_SELF, "denom = 0.0 \n")); }
314     delta[0] = (b[0] * A[3] - b[1] * A[1]) / denom;
315     delta[1] = (A[0] * b[1] - A[2] * b[0]) / denom;
316 
317     /* Find a temp (u,v) and associated objective function */
318     uvt[0] = uvs[0] + delta[0];
319     uvt[1] = uvs[1] + delta[1];
320 
321     if (uvt[0] < range[0]) {
322       uvt[0]   = range[0];
323       delta[0] = uvt[0] - uvs[0];
324     }
325     if (uvt[0] > range[1]) {
326       uvt[0]   = range[1];
327       delta[0] = uvt[0] - uvs[0];
328     }
329     if (uvt[1] < range[2]) {
330       uvt[1]   = range[2];
331       delta[1] = uvt[1] - uvs[1];
332     }
333     if (uvt[1] > range[3]) {
334       uvt[1]   = range[3];
335       delta[1] = uvt[1] - uvs[1];
336     }
337 
338     PetscCall(EG_evaluate(obj, uvt, data));
339 
340     obj_tmp = (coords[v * dE + 0] - data[0]) * (coords[v * dE + 0] - data[0]) + (coords[v * dE + 1] - data[1]) * (coords[v * dE + 1] - data[1]) + (coords[v * dE + 2] - data[2]) * (coords[v * dE + 2] - data[2]);
341 
342     /* If step is better, accept it and halve lambda (making it more Newton-like) */
343     if (obj_tmp < obj_old) {
344       obj_old = obj_tmp;
345       uvs[0]  = uvt[0];
346       uvs[1]  = uvt[1];
347       for (int jj = 0; jj < 18; ++jj) eval[jj] = data[jj];
348       lambda /= 2.0;
349       if (lambda < 1.0E-14) lambda = 1.0E-14;
350       if (obj_old < target) {
351         tolr = obj_old;
352         break;
353       }
354     } else {
355       /* Otherwise reject it and double lambda (making it more gradient-descent like) */
356       lambda *= 2.0;
357     }
358 
359     if (sqrt(delta[0] * delta[0] + delta[1] * delta[1]) < target) {
360       tolr = obj_old;
361       break;
362     }
363 
364     tolr = obj_old;
365 
366     loopCntr += 1;
367     if (loopCntr > 100) break;
368   }
369   paramsV[v * 3 + 0] = uvs[0];
370   paramsV[v * 3 + 1] = uvs[1];
371   PetscFunctionReturn(PETSC_SUCCESS);
372 }
373 
374 PetscErrorCode DMPlex_Geom_FACE_XYZtoUV_Internal(const PetscScalar coords[], ego obj, const PetscScalar range[], const PetscInt v, const PetscInt dE, PetscScalar paramsV[], PetscBool islite)
375 {
376   PetscInt    loopCntr = 0;
377   PetscScalar dx, dy, dz, lambda, tolr, denom, obj_old, obj_tmp, target;
378   PetscScalar uvs[2], uvt[2], delta[2], A[4], b[2], eval[18], data[18];
379 
380   PetscFunctionBeginHot;
381   /* Initialize Levenberg-Marquardt parameters */
382   lambda = 1.0;
383   tolr   = 1.0;
384   target = 1.0E-20;
385   uvs[0] = (range[0] + range[1]) / 2.;
386   uvs[1] = (range[2] + range[3]) / 2.;
387 
388   while (tolr >= target) {
389     if (islite) {
390       PetscCallEGADS(EGlite_evaluate, (obj, uvs, eval));
391     } else {
392       PetscCallEGADS(EG_evaluate, (obj, uvs, eval));
393     }
394 
395     dx      = coords[v * dE + 0] - eval[0];
396     dy      = coords[v * dE + 1] - eval[1];
397     dz      = coords[v * dE + 2] - eval[2];
398     obj_old = dx * dx + dy * dy + dz * dz;
399 
400     if (obj_old < target) {
401       tolr = obj_old;
402       break;
403     }
404 
405     A[0] = (eval[3] * eval[3] + eval[4] * eval[4] + eval[5] * eval[5]) * (1.0 + lambda);
406     A[1] = eval[3] * eval[6] + eval[4] * eval[7] + eval[5] * eval[8];
407     A[2] = A[1];
408     A[3] = (eval[6] * eval[6] + eval[7] * eval[7] + eval[8] * eval[8]) * (1.0 + lambda);
409 
410     b[0] = eval[3] * dx + eval[4] * dy + eval[5] * dz;
411     b[1] = eval[6] * dx + eval[7] * dy + eval[8] * dz;
412 
413     /* Solve A*delta = b using Cramer's Rule */
414     denom = A[0] * A[3] - A[2] * A[1];
415     if (denom == 0.0) { PetscCall(PetscPrintf(PETSC_COMM_SELF, "denom = 0.0 \n")); }
416     delta[0] = (b[0] * A[3] - b[1] * A[1]) / denom;
417     delta[1] = (A[0] * b[1] - A[2] * b[0]) / denom;
418 
419     /* Find a temp (u,v) and associated objective function */
420     uvt[0] = uvs[0] + delta[0];
421     uvt[1] = uvs[1] + delta[1];
422 
423     if (uvt[0] < range[0]) {
424       uvt[0]   = range[0];
425       delta[0] = uvt[0] - uvs[0];
426     }
427     if (uvt[0] > range[1]) {
428       uvt[0]   = range[1];
429       delta[0] = uvt[0] - uvs[0];
430     }
431     if (uvt[1] < range[2]) {
432       uvt[1]   = range[2];
433       delta[1] = uvt[1] - uvs[1];
434     }
435     if (uvt[1] > range[3]) {
436       uvt[1]   = range[3];
437       delta[1] = uvt[1] - uvs[1];
438     }
439 
440     if (islite) {
441       PetscCall(EGlite_evaluate(obj, uvt, data));
442     } else {
443       PetscCall(EG_evaluate(obj, uvt, data));
444     }
445 
446     obj_tmp = (coords[v * dE + 0] - data[0]) * (coords[v * dE + 0] - data[0]) + (coords[v * dE + 1] - data[1]) * (coords[v * dE + 1] - data[1]) + (coords[v * dE + 2] - data[2]) * (coords[v * dE + 2] - data[2]);
447 
448     /* If step is better, accept it and halve lambda (making it more Newton-like) */
449     if (obj_tmp < obj_old) {
450       obj_old = obj_tmp;
451       uvs[0]  = uvt[0];
452       uvs[1]  = uvt[1];
453       for (int jj = 0; jj < 18; ++jj) eval[jj] = data[jj];
454       lambda /= 2.0;
455       if (lambda < 1.0E-14) lambda = 1.0E-14;
456       if (obj_old < target) {
457         tolr = obj_old;
458         break;
459       }
460     } else {
461       /* Otherwise reject it and double lambda (making it more gradient-descent like) */
462       lambda *= 2.0;
463     }
464 
465     if (sqrt(delta[0] * delta[0] + delta[1] * delta[1]) < target) {
466       tolr = obj_old;
467       break;
468     }
469 
470     tolr = obj_old;
471 
472     loopCntr += 1;
473     if (loopCntr > 100) break;
474   }
475   paramsV[v * 3 + 0] = uvs[0];
476   paramsV[v * 3 + 1] = uvs[1];
477   PetscFunctionReturn(PETSC_SUCCESS);
478 }
479 
480 PetscErrorCode DMSnapToGeomModel_EGADS_Internal(DM dm, PetscInt p, ego model, PetscInt bodyID, PetscInt faceID, PetscInt edgeID, const PetscScalar mcoords[], PetscScalar gcoords[], PetscBool islite)
481 {
482   /* PETSc Variables */
483   DM   cdm;
484   ego *bodies;
485   ego  geom, body, obj;
486   /* result has to hold derivatives, along with the value */
487   double       params[3], result[18], paramsV[16 * 3], range[4];
488   int          Nb, oclass, mtype, *senses, peri;
489   Vec          coordinatesLocal;
490   PetscScalar *coords = NULL;
491   PetscInt     Nv, v, Np = 0, pm;
492   PetscInt     dE, d;
493   PetscReal    pTolr = 1.0e-14;
494 
495   PetscFunctionBeginHot;
496   PetscCall(DMGetCoordinateDM(dm, &cdm));
497   PetscCall(DMGetCoordinateDim(dm, &dE));
498   PetscCall(DMGetCoordinatesLocal(dm, &coordinatesLocal));
499 
500   if (islite) {
501     PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
502   } else {
503     PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
504   }
505 
506   PetscCheck(bodyID < Nb, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Body %" PetscInt_FMT " is not in [0, %" PetscInt_FMT ")", bodyID, Nb);
507   body = bodies[bodyID];
508 
509   if (edgeID >= 0) {
510     if (islite) {
511       PetscCall(EGlite_objectBodyTopo(body, EDGE, edgeID, &obj));
512       Np = 1;
513     } else {
514       PetscCall(EG_objectBodyTopo(body, EDGE, edgeID, &obj));
515       Np = 1;
516     }
517   } else if (faceID >= 0) {
518     if (islite) {
519       PetscCall(EGlite_objectBodyTopo(body, FACE, faceID, &obj));
520       Np = 2;
521     } else {
522       PetscCall(EG_objectBodyTopo(body, FACE, faceID, &obj));
523       Np = 2;
524     }
525   } else {
526     for (d = 0; d < dE; ++d) gcoords[d] = mcoords[d];
527     PetscFunctionReturn(PETSC_SUCCESS);
528   }
529 
530   /* Calculate parameters (t or u,v) for vertices */
531   PetscCall(DMPlexVecGetClosure(cdm, NULL, coordinatesLocal, p, &Nv, &coords));
532   Nv /= dE;
533   if (Nv == 1) {
534     PetscCall(DMPlexVecRestoreClosure(cdm, NULL, coordinatesLocal, p, &Nv, &coords));
535     for (d = 0; d < dE; ++d) gcoords[d] = mcoords[d];
536     PetscFunctionReturn(PETSC_SUCCESS);
537   }
538   PetscCheck(Nv <= 16, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot handle %" PetscInt_FMT " coordinates associated to point %" PetscInt_FMT, Nv, p);
539 
540   /* Correct EGADS/EGADSlite 2pi bug when calculating nearest point on Periodic Surfaces */
541   if (islite) {
542     PetscCall(EGlite_getRange(obj, range, &peri));
543   } else {
544     PetscCall(EG_getRange(obj, range, &peri));
545   }
546 
547   for (v = 0; v < Nv; ++v) {
548     if (edgeID > 0) {
549       PetscCall(DMPlex_Geom_EDGE_XYZtoUV_Internal(coords, obj, range, v, dE, paramsV, islite));
550     } else {
551       PetscCall(DMPlex_Geom_FACE_XYZtoUV_Internal(coords, obj, range, v, dE, paramsV, islite));
552     }
553   }
554   PetscCall(DMPlexVecRestoreClosure(cdm, NULL, coordinatesLocal, p, &Nv, &coords));
555 
556   /* Calculate parameters (t or u,v) for new vertex at edge midpoint */
557   for (pm = 0; pm < Np; ++pm) {
558     params[pm] = 0.;
559     for (v = 0; v < Nv; ++v) params[pm] += paramsV[v * 3 + pm];
560     params[pm] /= Nv;
561   }
562   PetscCheck((params[0] + pTolr >= range[0]) || (params[0] - pTolr <= range[1]), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Point %" PetscInt_FMT " had bad interpolation", p);
563   PetscCheck(Np < 2 || ((params[1] + pTolr >= range[2]) || (params[1] - pTolr <= range[3])), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Point %d had bad interpolation on v", p);
564 
565   /* Put coordinates for new vertex in result[] */
566   if (islite) {
567     PetscCall(EGlite_evaluate(obj, params, result));
568   } else {
569     PetscCall(EG_evaluate(obj, params, result));
570   }
571 
572   for (d = 0; d < dE; ++d) gcoords[d] = result[d];
573   PetscFunctionReturn(PETSC_SUCCESS);
574 }
575 #endif
576 
577 PetscErrorCode DMSnapToGeomModel_EGADS(DM dm, PetscInt p, PetscInt dE, const PetscScalar mcoords[], PetscScalar gcoords[])
578 {
579   PetscFunctionBeginHot;
580 #ifdef PETSC_HAVE_EGADS
581   DMLabel        bodyLabel, faceLabel, edgeLabel;
582   PetscInt       bodyID, faceID, edgeID;
583   PetscContainer modelObj;
584   ego            model;
585   PetscBool      islite = PETSC_FALSE;
586 
587   // FIXME: Change -dm_plex_refine_without_snap_to_geom to DM to shut off snapping
588   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
589   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
590   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
591   PetscCheck(bodyLabel && faceLabel && edgeLabel, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "EGADS meshes must have body, face, and edge labels defined");
592   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
593   if (!modelObj) {
594     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
595     islite = PETSC_TRUE;
596   }
597   PetscCheck(modelObj, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "EGADS mesh missing model object");
598 
599   PetscCall(PetscContainerGetPointer(modelObj, (void **)&model));
600   PetscCall(DMLabelGetValue(bodyLabel, p, &bodyID));
601   PetscCall(DMLabelGetValue(faceLabel, p, &faceID));
602   PetscCall(DMLabelGetValue(edgeLabel, p, &edgeID));
603   /* Allows for "Connective" Plex Edges present in models with multiple non-touching Entities */
604   if (bodyID < 0) {
605     for (PetscInt d = 0; d < dE; ++d) gcoords[d] = mcoords[d];
606     PetscFunctionReturn(PETSC_SUCCESS);
607   }
608   PetscCall(DMSnapToGeomModel_EGADS_Internal(dm, p, model, bodyID, faceID, edgeID, mcoords, gcoords, islite));
609 #endif
610   PetscFunctionReturn(PETSC_SUCCESS);
611 }
612 
613 #if defined(PETSC_HAVE_EGADS)
614 PetscErrorCode DMPlexGeomPrintModel_Internal(ego model, PetscBool islite)
615 {
616   /* PETSc Variables */
617   ego geom, *bodies, *nobjs, *mobjs, *lobjs, *shobjs, *fobjs, *eobjs;
618   int oclass, mtype, *senses, *shsenses, *fsenses, *lsenses, *esenses;
619   int Nb, b;
620 
621   PetscFunctionBeginUser;
622   /* test bodyTopo functions */
623   if (islite) {
624     PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
625   } else {
626     PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
627   }
628   PetscCall(PetscPrintf(PETSC_COMM_SELF, " Number of BODIES (nbodies): %" PetscInt_FMT " \n", Nb));
629 
630   for (b = 0; b < Nb; ++b) {
631     ego body = bodies[b];
632     int id, sh, Nsh, f, Nf, l, Nl, e, Ne, v, Nv;
633 
634     /* List Topology of Bodies */
635     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
636     PetscCall(PetscPrintf(PETSC_COMM_SELF, "   BODY %d TOPOLOGY SUMMARY \n", b));
637 
638     /* Output Basic Model Topology */
639     if (islite) {
640       PetscCall(EGlite_getBodyTopos(body, NULL, SHELL, &Nsh, &shobjs));
641     } else {
642       PetscCall(EG_getBodyTopos(body, NULL, SHELL, &Nsh, &shobjs));
643     }
644     PetscCall(PetscPrintf(PETSC_COMM_SELF, "      Number of SHELLS: %d \n", Nsh));
645 
646     if (islite) {
647       PetscCall(EGlite_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
648     } else {
649       PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
650     }
651     PetscCall(PetscPrintf(PETSC_COMM_SELF, "      Number of FACES: %d \n", Nf));
652 
653     if (islite) {
654       PetscCall(EGlite_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
655     } else {
656       PetscCall(EG_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
657     }
658     PetscCall(PetscPrintf(PETSC_COMM_SELF, "      Number of LOOPS: %d \n", Nl));
659 
660     if (islite) {
661       PetscCall(EGlite_getBodyTopos(body, NULL, EDGE, &Ne, &eobjs));
662     } else {
663       PetscCall(EG_getBodyTopos(body, NULL, EDGE, &Ne, &eobjs));
664     }
665     PetscCall(PetscPrintf(PETSC_COMM_SELF, "      Number of EDGES: %d \n", Ne));
666 
667     if (islite) {
668       PetscCall(EGlite_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
669     } else {
670       PetscCall(EG_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
671     }
672     PetscCall(PetscPrintf(PETSC_COMM_SELF, "      Number of NODES: %d \n", Nv));
673 
674     if (islite) {
675       EGlite_free(shobjs);
676       EGlite_free(fobjs);
677       EGlite_free(lobjs);
678       EGlite_free(eobjs);
679       EGlite_free(nobjs);
680     } else {
681       EG_free(shobjs);
682       EG_free(fobjs);
683       EG_free(lobjs);
684       EG_free(eobjs);
685       EG_free(nobjs);
686     }
687 
688     /* List Topology of Bodies */
689     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
690     PetscCall(PetscPrintf(PETSC_COMM_SELF, "      BODY %d TOPOLOGY DETAILS \n", b));
691 
692     /* Get SHELL info which associated with the current BODY */
693     if (islite) {
694       PetscCall(EGlite_getTopology(body, &geom, &oclass, &mtype, NULL, &Nsh, &shobjs, &shsenses));
695     } else {
696       PetscCall(EG_getTopology(body, &geom, &oclass, &mtype, NULL, &Nsh, &shobjs, &shsenses));
697     }
698 
699     for (sh = 0; sh < Nsh; ++sh) {
700       ego shell   = shobjs[sh];
701       int shsense = shsenses[sh];
702 
703       if (islite) {
704         id = EGlite_indexBodyTopo(body, shell);
705       } else {
706         id = EG_indexBodyTopo(body, shell);
707       }
708       PetscCall(PetscPrintf(PETSC_COMM_SELF, "         SHELL ID: %d :: sense = %d\n", id, shsense));
709 
710       /* Get FACE information associated with current SHELL */
711       if (islite) {
712         PetscCall(EGlite_getTopology(shell, &geom, &oclass, &mtype, NULL, &Nf, &fobjs, &fsenses));
713       } else {
714         PetscCall(EG_getTopology(shell, &geom, &oclass, &mtype, NULL, &Nf, &fobjs, &fsenses));
715       }
716 
717       for (f = 0; f < Nf; ++f) {
718         ego     face = fobjs[f];
719         ego     gRef, gPrev, gNext;
720         int     goclass, gmtype, *gpinfo;
721         double *gprv;
722         char   *gClass = (char *)"", *gType = (char *)"";
723         double  fdata[4];
724         ego     fRef, fPrev, fNext;
725         int     foclass, fmtype;
726 
727         if (islite) {
728           id = EGlite_indexBodyTopo(body, face);
729         } else {
730           id = EG_indexBodyTopo(body, face);
731         }
732 
733         /* Get LOOP info associated with current FACE */
734         if (islite) {
735           PetscCall(EGlite_getTopology(face, &geom, &oclass, &mtype, fdata, &Nl, &lobjs, &lsenses));
736           PetscCall(EGlite_getInfo(face, &foclass, &fmtype, &fRef, &fPrev, &fNext));
737           PetscCall(EGlite_getGeometry(geom, &goclass, &gmtype, &gRef, &gpinfo, &gprv));
738           PetscCall(EGlite_getInfo(geom, &goclass, &gmtype, &gRef, &gPrev, &gNext));
739         } else {
740           PetscCall(EG_getTopology(face, &geom, &oclass, &mtype, fdata, &Nl, &lobjs, &lsenses));
741           PetscCall(EG_getInfo(face, &foclass, &fmtype, &fRef, &fPrev, &fNext));
742           PetscCall(EG_getGeometry(geom, &goclass, &gmtype, &gRef, &gpinfo, &gprv));
743           PetscCall(EG_getInfo(geom, &goclass, &gmtype, &gRef, &gPrev, &gNext));
744         }
745 
746         PetscCall(DMPlex_EGADS_GeomDecode_Internal(goclass, gmtype, &gClass, &gType));
747         PetscCall(PetscPrintf(PETSC_COMM_SELF, "           FACE ID: %d :: sense = %d \n", id, fmtype));
748         PetscCall(PetscPrintf(PETSC_COMM_SELF, "             GEOMETRY CLASS: %s \n", gClass));
749         PetscCall(PetscPrintf(PETSC_COMM_SELF, "             GEOMETRY TYPE:  %s \n\n", gType));
750         PetscCall(PetscPrintf(PETSC_COMM_SELF, "             RANGE (umin, umax) = (%f, %f) \n", fdata[0], fdata[1]));
751         PetscCall(PetscPrintf(PETSC_COMM_SELF, "                   (vmin, vmax) = (%f, %f) \n\n", fdata[2], fdata[3]));
752 
753         for (l = 0; l < Nl; ++l) {
754           ego loop   = lobjs[l];
755           int lsense = lsenses[l];
756 
757           if (islite) {
758             id = EGlite_indexBodyTopo(body, loop);
759           } else {
760             id = EG_indexBodyTopo(body, loop);
761           }
762 
763           PetscCall(PetscPrintf(PETSC_COMM_SELF, "             LOOP ID: %d :: sense = %d\n", id, lsense));
764 
765           /* Get EDGE info associated with the current LOOP */
766           if (islite) {
767             PetscCall(EGlite_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &eobjs, &esenses));
768           } else {
769             PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &eobjs, &esenses));
770           }
771 
772           for (e = 0; e < Ne; ++e) {
773             ego    edge = eobjs[e];
774             ego    topRef, prev, next;
775             int    esense   = esenses[e];
776             double range[4] = {0., 0., 0., 0.};
777             int    peri;
778             ego    gEref, gEprev, gEnext;
779             int    gEoclass, gEmtype;
780             char  *gEclass = (char *)"", *gEtype = (char *)"";
781 
782             if (islite) {
783               PetscCall(EGlite_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
784               if (mtype != DEGENERATE) { PetscCall(EGlite_getInfo(geom, &gEoclass, &gEmtype, &gEref, &gEprev, &gEnext)); }
785             } else {
786               PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
787               PetscCall(EG_getInfo(geom, &gEoclass, &gEmtype, &gEref, &gEprev, &gEnext));
788             }
789 
790             if (mtype != DEGENERATE) { PetscCall(DMPlex_EGADS_GeomDecode_Internal(gEoclass, gEmtype, &gEclass, &gEtype)); }
791 
792             if (islite) {
793               id = EGlite_indexBodyTopo(body, edge);
794               PetscCall(EGlite_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
795             } else {
796               id = EG_indexBodyTopo(body, edge);
797               PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
798             }
799 
800             PetscCall(PetscPrintf(PETSC_COMM_SELF, "               EDGE ID: %d :: sense = %d\n", id, esense));
801             if (mtype != DEGENERATE) {
802               PetscCall(PetscPrintf(PETSC_COMM_SELF, "                 GEOMETRY CLASS: %s \n", gEclass));
803               PetscCall(PetscPrintf(PETSC_COMM_SELF, "                 GEOMETRY TYPE:  %s \n", gEtype));
804             }
805 
806             if (mtype == DEGENERATE) { PetscCall(PetscPrintf(PETSC_COMM_SELF, "                 EDGE %d is DEGENERATE \n", id)); }
807 
808             if (islite) {
809               PetscCall(EGlite_getRange(edge, range, &peri));
810             } else {
811               PetscCall(EG_getRange(edge, range, &peri));
812             }
813 
814             PetscCall(PetscPrintf(PETSC_COMM_SELF, "                 Peri = %d :: Range = %lf, %lf, %lf, %lf \n", peri, range[0], range[1], range[2], range[3]));
815 
816             /* Get NODE info associated with the current EDGE */
817             if (islite) {
818               PetscCall(EGlite_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
819             } else {
820               PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
821             }
822 
823             for (v = 0; v < Nv; ++v) {
824               ego    vertex = nobjs[v];
825               double limits[4];
826               int    dummy;
827 
828               if (islite) {
829                 PetscCall(EGlite_getTopology(vertex, &geom, &oclass, &mtype, limits, &dummy, &mobjs, &senses));
830                 id = EGlite_indexBodyTopo(body, vertex);
831               } else {
832                 PetscCall(EG_getTopology(vertex, &geom, &oclass, &mtype, limits, &dummy, &mobjs, &senses));
833                 id = EG_indexBodyTopo(body, vertex);
834               }
835               PetscCall(PetscPrintf(PETSC_COMM_SELF, "                 NODE ID: %d \n", id));
836               PetscCall(PetscPrintf(PETSC_COMM_SELF, "                    (x, y, z) = (%lf, %lf, %lf) \n", limits[0], limits[1], limits[2]));
837             }
838           }
839         }
840       }
841     }
842   }
843   PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n\n"));
844   PetscFunctionReturn(PETSC_SUCCESS);
845 }
846 
847 static PetscErrorCode DMPlexEGADSDestroy_Private(void **context)
848 {
849   if (*context) EG_deleteObject((ego)*context);
850   return (PETSC_SUCCESS);
851 }
852 
853 static PetscErrorCode DMPlexEGADSClose_Private(void **context)
854 {
855   if (*context) EG_close((ego)*context);
856   return (PETSC_SUCCESS);
857 }
858 
859 PetscErrorCode DMPlexEGADSliteDestroy_Private(void **context)
860 {
861   if (*context) EGlite_deleteObject((ego)*context);
862   return 0;
863 }
864 
865 PetscErrorCode DMPlexEGADSliteClose_Private(void **context)
866 {
867   if (*context) EGlite_close((ego)*context);
868   return 0;
869 }
870 
871 PetscErrorCode DMPlexCreateGeom_Internal(MPI_Comm comm, ego context, ego model, DM *newdm, PetscBool islite)
872 {
873   /* EGADS variables */
874   ego geom, *bodies, *objs, *nobjs, *mobjs, *lobjs;
875   int oclass, mtype, nbodies, *senses;
876   int b;
877   /* PETSc variables */
878   DM          dm;
879   DMLabel     bodyLabel, faceLabel, edgeLabel, vertexLabel;
880   PetscHMapI  edgeMap = NULL;
881   PetscInt    cStart, cEnd, c;
882   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;
883   PetscInt   *cells = NULL, *cone = NULL;
884   PetscReal  *coords = NULL;
885   PetscMPIInt rank;
886 
887   PetscFunctionBegin;
888   PetscCallMPI(MPI_Comm_rank(comm, &rank));
889   if (rank == 0) {
890     const PetscInt debug = 0;
891 
892     /* ---------------------------------------------------------------------------------------------------
893     Generate PETSc DMPlex
894       Get all Nodes in model, record coordinates in a correctly formatted array
895       Cycle through bodies, cycle through loops, recorde NODE IDs in a correctly formatted array
896       We need to uniformly refine the initial geometry to guarantee a valid mesh
897     */
898 
899     /* Calculate cell and vertex sizes */
900     if (islite) {
901       PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, &nbodies, &bodies, &senses));
902     } else {
903       PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &nbodies, &bodies, &senses));
904     }
905 
906     PetscCall(PetscHMapICreate(&edgeMap));
907     numEdges = 0;
908     for (b = 0; b < nbodies; ++b) {
909       ego body = bodies[b];
910       int id, Nl, l, Nv, v;
911 
912       if (islite) {
913         PetscCall(EGlite_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
914       } else {
915         PetscCall(EG_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
916       }
917 
918       for (l = 0; l < Nl; ++l) {
919         ego loop = lobjs[l];
920         int Ner  = 0, Ne, e, Nc;
921 
922         if (islite) {
923           PetscCall(EGlite_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));
924         } else {
925           PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));
926         }
927 
928         for (e = 0; e < Ne; ++e) {
929           ego           edge = objs[e];
930           int           Nv, id;
931           PetscHashIter iter;
932           PetscBool     found;
933 
934           if (islite) {
935             PetscCall(EGlite_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
936           } else {
937             PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
938           }
939 
940           if (mtype == DEGENERATE) continue;
941 
942           if (islite) {
943             id = EGlite_indexBodyTopo(body, edge);
944           } else {
945             id = EG_indexBodyTopo(body, edge);
946           }
947 
948           PetscCall(PetscHMapIFind(edgeMap, id - 1, &iter, &found));
949           if (!found) { PetscCall(PetscHMapISet(edgeMap, id - 1, numEdges++)); }
950           ++Ner;
951         }
952         if (Ner == 2) {
953           Nc = 2;
954         } else if (Ner == 3) {
955           Nc = 4;
956         } else if (Ner == 4) {
957           Nc = 8;
958           ++numQuads;
959         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot support loop with %d edges", Ner);
960         numCells += Nc;
961         newCells += Nc - 1;
962         maxCorners = PetscMax(Ner * 2 + 1, maxCorners);
963       }
964       if (islite) {
965         PetscCall(EGlite_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
966       } else {
967         PetscCall(EG_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
968       }
969 
970       for (v = 0; v < Nv; ++v) {
971         ego vertex = nobjs[v];
972 
973         if (islite) {
974           id = EGlite_indexBodyTopo(body, vertex);
975         } else {
976           id = EG_indexBodyTopo(body, vertex);
977         }
978         /* TODO: Instead of assuming contiguous ids, we could use a hash table */
979         numVertices = PetscMax(id, numVertices);
980       }
981       if (islite) {
982         EGlite_free(lobjs);
983         EGlite_free(nobjs);
984       } else {
985         EG_free(lobjs);
986         EG_free(nobjs);
987       }
988     }
989     PetscCall(PetscHMapIGetSize(edgeMap, &numEdges));
990     newVertices = numEdges + numQuads;
991     numVertices += newVertices;
992 
993     dim        = 2; /* Assume 3D Models :: Need to update to handle 2D Models in the future */
994     cdim       = 3; /* Assume 3D Models :: Need to update to handle 2D Models in the future */
995     numCorners = 3; /* Split cells into triangles */
996     PetscCall(PetscMalloc3(numVertices * cdim, &coords, numCells * numCorners, &cells, maxCorners, &cone));
997 
998     /* Get vertex coordinates */
999     for (b = 0; b < nbodies; ++b) {
1000       ego body = bodies[b];
1001       int id, Nv, v;
1002 
1003       if (islite) {
1004         PetscCall(EGlite_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
1005       } else {
1006         PetscCall(EG_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
1007       }
1008 
1009       for (v = 0; v < Nv; ++v) {
1010         ego    vertex = nobjs[v];
1011         double limits[4];
1012         int    dummy;
1013 
1014         if (islite) {
1015           PetscCall(EGlite_getTopology(vertex, &geom, &oclass, &mtype, limits, &dummy, &mobjs, &senses));
1016           id = EGlite_indexBodyTopo(body, vertex);
1017         } else {
1018           PetscCall(EG_getTopology(vertex, &geom, &oclass, &mtype, limits, &dummy, &mobjs, &senses));
1019           id = EG_indexBodyTopo(body, vertex);
1020         }
1021 
1022         coords[(id - 1) * cdim + 0] = limits[0];
1023         coords[(id - 1) * cdim + 1] = limits[1];
1024         coords[(id - 1) * cdim + 2] = limits[2];
1025       }
1026       if (islite) {
1027         EGlite_free(nobjs);
1028       } else {
1029         EG_free(nobjs);
1030       }
1031     }
1032     PetscCall(PetscHMapIClear(edgeMap));
1033     fOff     = numVertices - newVertices + numEdges;
1034     numEdges = 0;
1035     numQuads = 0;
1036     for (b = 0; b < nbodies; ++b) {
1037       ego body = bodies[b];
1038       int Nl, l;
1039 
1040       if (islite) {
1041         PetscCall(EGlite_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
1042       } else {
1043         PetscCall(EG_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
1044       }
1045 
1046       for (l = 0; l < Nl; ++l) {
1047         ego loop = lobjs[l];
1048         int lid, Ner = 0, Ne, e;
1049 
1050         if (islite) {
1051           lid = EGlite_indexBodyTopo(body, loop);
1052           PetscCall(EGlite_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));
1053         } else {
1054           lid = EG_indexBodyTopo(body, loop);
1055           PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));
1056         }
1057 
1058         for (e = 0; e < Ne; ++e) {
1059           ego           edge = objs[e];
1060           int           eid, Nv;
1061           PetscHashIter iter;
1062           PetscBool     found;
1063 
1064           if (islite) {
1065             PetscCall(EGlite_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
1066           } else {
1067             PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
1068           }
1069 
1070           if (mtype == DEGENERATE) continue;
1071           ++Ner;
1072 
1073           if (islite) {
1074             eid = EGlite_indexBodyTopo(body, edge);
1075           } else {
1076             eid = EG_indexBodyTopo(body, edge);
1077           }
1078 
1079           PetscCall(PetscHMapIFind(edgeMap, eid - 1, &iter, &found));
1080           if (!found) {
1081             PetscInt v = numVertices - newVertices + numEdges;
1082             double   range[4], params[3] = {0., 0., 0.}, result[18];
1083             int      periodic[2];
1084 
1085             PetscCall(PetscHMapISet(edgeMap, eid - 1, numEdges++));
1086 
1087             if (islite) {
1088               PetscCall(EGlite_getRange(edge, range, periodic));
1089             } else {
1090               PetscCall(EG_getRange(edge, range, periodic));
1091             }
1092 
1093             params[0] = 0.5 * (range[0] + range[1]);
1094             if (islite) {
1095               PetscCall(EGlite_evaluate(edge, params, result));
1096             } else {
1097               PetscCall(EG_evaluate(edge, params, result));
1098             }
1099             coords[v * cdim + 0] = result[0];
1100             coords[v * cdim + 1] = result[1];
1101             coords[v * cdim + 2] = result[2];
1102           }
1103         }
1104         if (Ner == 4) {
1105           PetscInt v = fOff + numQuads++;
1106           ego     *fobjs, face;
1107           double   range[4], params[3] = {0., 0., 0.}, result[18];
1108           int      Nf, fid, periodic[2];
1109 
1110           if (islite) {
1111             PetscCall(EGlite_getBodyTopos(body, loop, FACE, &Nf, &fobjs));
1112           } else {
1113             PetscCall(EG_getBodyTopos(body, loop, FACE, &Nf, &fobjs));
1114           }
1115           face = fobjs[0];
1116 
1117           if (islite) {
1118             fid = EGlite_indexBodyTopo(body, face);
1119           } else {
1120             fid = EG_indexBodyTopo(body, face);
1121           }
1122 
1123           PetscCheck(Nf != 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Loop %d has %" PetscInt_FMT " faces, instead of 1 (%" PetscInt_FMT ")", lid - 1, Nf, fid);
1124           if (islite) {
1125             PetscCall(EGlite_getRange(face, range, periodic));
1126           } else {
1127             PetscCall(EG_getRange(face, range, periodic));
1128           }
1129           params[0] = 0.5 * (range[0] + range[1]);
1130           params[1] = 0.5 * (range[2] + range[3]);
1131           if (islite) {
1132             PetscCall(EGlite_evaluate(face, params, result));
1133           } else {
1134             PetscCall(EG_evaluate(face, params, result));
1135           }
1136           coords[v * cdim + 0] = result[0];
1137           coords[v * cdim + 1] = result[1];
1138           coords[v * cdim + 2] = result[2];
1139         }
1140       }
1141     }
1142     PetscCheck(numEdges + numQuads == newVertices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of new vertices %d != %d previous count", numEdges + numQuads, newVertices);
1143 
1144     /* Get cell vertices by traversing loops */
1145     numQuads = 0;
1146     cOff     = 0;
1147     for (b = 0; b < nbodies; ++b) {
1148       ego body = bodies[b];
1149       int id, Nl, l;
1150 
1151       if (islite) {
1152         PetscCall(EGlite_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
1153       } else {
1154         PetscCall(EG_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
1155       }
1156       for (l = 0; l < Nl; ++l) {
1157         ego loop = lobjs[l];
1158         int lid, Ner = 0, Ne, e, nc = 0, c, Nt, t;
1159 
1160         if (islite) {
1161           lid = EGlite_indexBodyTopo(body, loop);
1162           PetscCall(EGlite_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));
1163         } else {
1164           lid = EG_indexBodyTopo(body, loop);
1165           PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));
1166         }
1167 
1168         for (e = 0; e < Ne; ++e) {
1169           ego edge = objs[e];
1170           int points[3];
1171           int eid, Nv, v, tmp;
1172 
1173           if (islite) {
1174             eid = EGlite_indexBodyTopo(body, edge);
1175             PetscCall(EGlite_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
1176           } else {
1177             eid = EG_indexBodyTopo(body, edge);
1178             PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
1179           }
1180 
1181           if (mtype == DEGENERATE) continue;
1182           else ++Ner;
1183           PetscCheck(Nv == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Edge %" PetscInt_FMT " has %" PetscInt_FMT " vertices != 2", eid, Nv);
1184 
1185           for (v = 0; v < Nv; ++v) {
1186             ego vertex = nobjs[v];
1187 
1188             if (islite) {
1189               id = EGlite_indexBodyTopo(body, vertex);
1190             } else {
1191               id = EG_indexBodyTopo(body, vertex);
1192             }
1193             points[v * 2] = id - 1;
1194           }
1195           {
1196             PetscInt edgeNum;
1197 
1198             PetscCall(PetscHMapIGet(edgeMap, eid - 1, &edgeNum));
1199             points[1] = numVertices - newVertices + edgeNum;
1200           }
1201           /* EGADS loops are not oriented, but seem to be in order, so we must piece them together */
1202           if (!nc) {
1203             for (v = 0; v < Nv + 1; ++v) cone[nc++] = points[v];
1204           } else {
1205             if (cone[nc - 1] == points[0]) {
1206               cone[nc++] = points[1];
1207               if (cone[0] != points[2]) cone[nc++] = points[2];
1208             } else if (cone[nc - 1] == points[2]) {
1209               cone[nc++] = points[1];
1210               if (cone[0] != points[0]) cone[nc++] = points[0];
1211             } else if (cone[nc - 3] == points[0]) {
1212               tmp          = cone[nc - 3];
1213               cone[nc - 3] = cone[nc - 1];
1214               cone[nc - 1] = tmp;
1215               cone[nc++]   = points[1];
1216               if (cone[0] != points[2]) cone[nc++] = points[2];
1217             } else if (cone[nc - 3] == points[2]) {
1218               tmp          = cone[nc - 3];
1219               cone[nc - 3] = cone[nc - 1];
1220               cone[nc - 1] = tmp;
1221               cone[nc++]   = points[1];
1222               if (cone[0] != points[0]) cone[nc++] = points[0];
1223             } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Edge %d does not match its predecessor", eid);
1224           }
1225         }
1226         PetscCheck(nc == 2 * Ner, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Number of corners %" PetscInt_FMT " != %" PetscInt_FMT, nc, 2 * Ner);
1227         if (Ner == 4) { cone[nc++] = numVertices - newVertices + numEdges + numQuads++; }
1228         PetscCheck(nc <= maxCorners, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Number of corners %" PetscInt_FMT " > %" PetscInt_FMT " max", nc, maxCorners);
1229         /* Triangulate the loop */
1230         switch (Ner) {
1231         case 2: /* Bi-Segment -> 2 triangles */
1232           Nt                           = 2;
1233           cells[cOff * numCorners + 0] = cone[0];
1234           cells[cOff * numCorners + 1] = cone[1];
1235           cells[cOff * numCorners + 2] = cone[2];
1236           ++cOff;
1237           cells[cOff * numCorners + 0] = cone[0];
1238           cells[cOff * numCorners + 1] = cone[2];
1239           cells[cOff * numCorners + 2] = cone[3];
1240           ++cOff;
1241           break;
1242         case 3: /* Triangle   -> 4 triangles */
1243           Nt                           = 4;
1244           cells[cOff * numCorners + 0] = cone[0];
1245           cells[cOff * numCorners + 1] = cone[1];
1246           cells[cOff * numCorners + 2] = cone[5];
1247           ++cOff;
1248           cells[cOff * numCorners + 0] = cone[1];
1249           cells[cOff * numCorners + 1] = cone[2];
1250           cells[cOff * numCorners + 2] = cone[3];
1251           ++cOff;
1252           cells[cOff * numCorners + 0] = cone[5];
1253           cells[cOff * numCorners + 1] = cone[3];
1254           cells[cOff * numCorners + 2] = cone[4];
1255           ++cOff;
1256           cells[cOff * numCorners + 0] = cone[1];
1257           cells[cOff * numCorners + 1] = cone[3];
1258           cells[cOff * numCorners + 2] = cone[5];
1259           ++cOff;
1260           break;
1261         case 4: /* Quad       -> 8 triangles */
1262           Nt                           = 8;
1263           cells[cOff * numCorners + 0] = cone[0];
1264           cells[cOff * numCorners + 1] = cone[1];
1265           cells[cOff * numCorners + 2] = cone[7];
1266           ++cOff;
1267           cells[cOff * numCorners + 0] = cone[1];
1268           cells[cOff * numCorners + 1] = cone[2];
1269           cells[cOff * numCorners + 2] = cone[3];
1270           ++cOff;
1271           cells[cOff * numCorners + 0] = cone[3];
1272           cells[cOff * numCorners + 1] = cone[4];
1273           cells[cOff * numCorners + 2] = cone[5];
1274           ++cOff;
1275           cells[cOff * numCorners + 0] = cone[5];
1276           cells[cOff * numCorners + 1] = cone[6];
1277           cells[cOff * numCorners + 2] = cone[7];
1278           ++cOff;
1279           cells[cOff * numCorners + 0] = cone[8];
1280           cells[cOff * numCorners + 1] = cone[1];
1281           cells[cOff * numCorners + 2] = cone[3];
1282           ++cOff;
1283           cells[cOff * numCorners + 0] = cone[8];
1284           cells[cOff * numCorners + 1] = cone[3];
1285           cells[cOff * numCorners + 2] = cone[5];
1286           ++cOff;
1287           cells[cOff * numCorners + 0] = cone[8];
1288           cells[cOff * numCorners + 1] = cone[5];
1289           cells[cOff * numCorners + 2] = cone[7];
1290           ++cOff;
1291           cells[cOff * numCorners + 0] = cone[8];
1292           cells[cOff * numCorners + 1] = cone[7];
1293           cells[cOff * numCorners + 2] = cone[1];
1294           ++cOff;
1295           break;
1296         default:
1297           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Loop %d has %d edges, which we do not support", lid, Ner);
1298         }
1299         if (debug) {
1300           for (t = 0; t < Nt; ++t) {
1301             PetscCall(PetscPrintf(PETSC_COMM_SELF, "  LOOP Corner NODEs Triangle %d (", t));
1302             for (c = 0; c < numCorners; ++c) {
1303               if (c > 0) { PetscCall(PetscPrintf(PETSC_COMM_SELF, ", ")); }
1304               PetscCall(PetscPrintf(PETSC_COMM_SELF, "%d", cells[(cOff - Nt + t) * numCorners + c]));
1305             }
1306             PetscCall(PetscPrintf(PETSC_COMM_SELF, ")\n"));
1307           }
1308         }
1309       }
1310       if (islite) {
1311         EGlite_free(lobjs);
1312       } else {
1313         EG_free(lobjs);
1314       }
1315     }
1316   }
1317   PetscCheck(cOff == numCells, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Count of total cells %d != %d previous count", cOff, numCells);
1318   PetscCall(DMPlexCreateFromCellListPetsc(PETSC_COMM_WORLD, dim, numCells, numVertices, numCorners, PETSC_TRUE, cells, cdim, coords, &dm));
1319   PetscCall(PetscFree3(coords, cells, cone));
1320   PetscCall(PetscInfo(dm, " Total Number of Unique Cells    = %d (%d)\n", numCells, newCells));
1321   PetscCall(PetscInfo(dm, " Total Number of Unique Vertices = %d (%d)\n", numVertices, newVertices));
1322   /* Embed EGADS model in DM */
1323   {
1324     PetscContainer modelObj, contextObj;
1325 
1326     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &modelObj));
1327     PetscCall(PetscContainerSetPointer(modelObj, model));
1328     PetscCall(PetscContainerSetCtxDestroy(modelObj, (PetscCtxDestroyFn *)DMPlexEGADSDestroy_Private));
1329     PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Model", (PetscObject)modelObj));
1330     PetscCall(PetscContainerDestroy(&modelObj));
1331 
1332     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &contextObj));
1333     PetscCall(PetscContainerSetPointer(contextObj, context));
1334     PetscCall(PetscContainerSetCtxDestroy(contextObj, (PetscCtxDestroyFn *)DMPlexEGADSClose_Private));
1335     PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Context", (PetscObject)contextObj));
1336     PetscCall(PetscContainerDestroy(&contextObj));
1337   }
1338   /* Label points */
1339   PetscCall(DMCreateLabel(dm, "EGADS Body ID"));
1340   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
1341   PetscCall(DMCreateLabel(dm, "EGADS Face ID"));
1342   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
1343   PetscCall(DMCreateLabel(dm, "EGADS Edge ID"));
1344   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
1345   PetscCall(DMCreateLabel(dm, "EGADS Vertex ID"));
1346   PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));
1347   cOff = 0;
1348   for (b = 0; b < nbodies; ++b) {
1349     ego body = bodies[b];
1350     int id, Nl, l;
1351 
1352     if (islite) {
1353       PetscCall(EGlite_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
1354     } else {
1355       PetscCall(EG_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
1356     }
1357     for (l = 0; l < Nl; ++l) {
1358       ego  loop = lobjs[l];
1359       ego *fobjs;
1360       int  lid, Nf, fid, Ner = 0, Ne, e, Nt = 0, t;
1361 
1362       if (islite) {
1363         lid = EGlite_indexBodyTopo(body, loop);
1364         PetscCall(EGlite_getBodyTopos(body, loop, FACE, &Nf, &fobjs));
1365       } else {
1366         lid = EG_indexBodyTopo(body, loop);
1367         PetscCall(EG_getBodyTopos(body, loop, FACE, &Nf, &fobjs));
1368       }
1369 
1370       PetscCheck(Nf <= 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Loop %d has %d > 1 faces, which is not supported", lid, Nf);
1371       if (islite) {
1372         fid = EGlite_indexBodyTopo(body, fobjs[0]);
1373         EGlite_free(fobjs);
1374         PetscCall(EGlite_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));
1375       } else {
1376         fid = EG_indexBodyTopo(body, fobjs[0]);
1377         EG_free(fobjs);
1378         PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));
1379       }
1380 
1381       for (e = 0; e < Ne; ++e) {
1382         ego             edge = objs[e];
1383         int             eid, Nv, v;
1384         PetscInt        points[3], support[2], numEdges, edgeNum;
1385         const PetscInt *edges;
1386 
1387         if (islite) {
1388           eid = EGlite_indexBodyTopo(body, edge);
1389           PetscCall(EGlite_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
1390         } else {
1391           eid = EG_indexBodyTopo(body, edge);
1392           PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
1393         }
1394 
1395         if (mtype == DEGENERATE) continue;
1396         else ++Ner;
1397         for (v = 0; v < Nv; ++v) {
1398           ego vertex = nobjs[v];
1399 
1400           if (islite) {
1401             id = EGlite_indexBodyTopo(body, vertex);
1402           } else {
1403             id = EG_indexBodyTopo(body, vertex);
1404           }
1405 
1406           PetscCall(DMLabelSetValue(edgeLabel, numCells + id - 1, eid));
1407           points[v * 2] = numCells + id - 1;
1408         }
1409         PetscCall(PetscHMapIGet(edgeMap, eid - 1, &edgeNum));
1410         points[1] = numCells + numVertices - newVertices + edgeNum;
1411 
1412         PetscCall(DMLabelSetValue(edgeLabel, points[1], eid));
1413         support[0] = points[0];
1414         support[1] = points[1];
1415         PetscCall(DMPlexGetJoin(dm, 2, support, &numEdges, &edges));
1416         PetscCheck(numEdges == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Vertices (%d, %d) should only bound 1 edge, not %d", support[0], support[1], numEdges);
1417         PetscCall(DMLabelSetValue(edgeLabel, edges[0], eid));
1418         PetscCall(DMPlexRestoreJoin(dm, 2, support, &numEdges, &edges));
1419         support[0] = points[1];
1420         support[1] = points[2];
1421         PetscCall(DMPlexGetJoin(dm, 2, support, &numEdges, &edges));
1422         PetscCheck(numEdges == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Vertices (%d, %d) should only bound 1 edge, not %d", support[0], support[1], numEdges);
1423         PetscCall(DMLabelSetValue(edgeLabel, edges[0], eid));
1424         PetscCall(DMPlexRestoreJoin(dm, 2, support, &numEdges, &edges));
1425       }
1426       switch (Ner) {
1427       case 2:
1428         Nt = 2;
1429         break;
1430       case 3:
1431         Nt = 4;
1432         break;
1433       case 4:
1434         Nt = 8;
1435         break;
1436       default:
1437         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Loop with %d edges is unsupported", Ner);
1438       }
1439       for (t = 0; t < Nt; ++t) {
1440         PetscCall(DMLabelSetValue(bodyLabel, cOff + t, b));
1441         PetscCall(DMLabelSetValue(faceLabel, cOff + t, fid));
1442       }
1443       cOff += Nt;
1444     }
1445     if (islite) {
1446       EGlite_free(lobjs);
1447     } else {
1448       EG_free(lobjs);
1449     }
1450   }
1451   PetscCall(PetscHMapIDestroy(&edgeMap));
1452   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1453   for (c = cStart; c < cEnd; ++c) {
1454     PetscInt *closure = NULL;
1455     PetscInt  clSize, cl, bval, fval;
1456 
1457     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
1458     PetscCall(DMLabelGetValue(bodyLabel, c, &bval));
1459     PetscCall(DMLabelGetValue(faceLabel, c, &fval));
1460     for (cl = 0; cl < clSize * 2; cl += 2) {
1461       PetscCall(DMLabelSetValue(bodyLabel, closure[cl], bval));
1462       PetscCall(DMLabelSetValue(faceLabel, closure[cl], fval));
1463     }
1464     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
1465   }
1466   *newdm = dm;
1467   PetscFunctionReturn(0);
1468 }
1469 
1470 PetscErrorCode DMPlexCreateGeom(MPI_Comm comm, ego context, ego model, DM *newdm, PetscBool islite)
1471 {
1472   // EGADS variables
1473   ego geom, *bodies, *mobjs, *fobjs, *lobjs, *eobjs, *nobjs;
1474   ego topRef, prev, next;
1475   int oclass, mtype, nbodies, *senses, *lSenses, *eSenses;
1476   int b;
1477   // PETSc variables
1478   DM              dm;
1479   DMLabel         bodyLabel, faceLabel, edgeLabel, vertexLabel;
1480   PetscHMapI      edgeMap = NULL, bodyIndexMap = NULL, bodyVertexMap = NULL, bodyEdgeMap = NULL, bodyFaceMap = NULL, bodyEdgeGlobalMap = NULL;
1481   PetscInt        dim = -1, cdim = -1, numCorners = 0, numVertices = 0, numEdges = 0, numFaces = 0, numCells = 0, edgeCntr = 0;
1482   PetscInt        cellCntr = 0, numPoints = 0;
1483   PetscInt       *cells  = NULL;
1484   const PetscInt *cone   = NULL;
1485   PetscReal      *coords = NULL;
1486   PetscMPIInt     rank;
1487 
1488   PetscFunctionBeginUser;
1489   PetscCallMPI(MPI_Comm_rank(comm, &rank));
1490   if (rank == 0) {
1491     // ---------------------------------------------------------------------------------------------------
1492     // Generate PETSc DMPlex
1493     //  Get all Nodes in model, record coordinates in a correctly formatted array
1494     //  Cycle through bodies, cycle through loops, recorde NODE IDs in a correctly formatted array
1495     //  We need to uniformly refine the initial geometry to guarantee a valid mesh
1496 
1497     // Calculate cell and vertex sizes
1498     if (islite) {
1499       PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, &nbodies, &bodies, &senses));
1500     } else {
1501       PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &nbodies, &bodies, &senses));
1502     }
1503     PetscCall(PetscHMapICreate(&edgeMap));
1504     PetscCall(PetscHMapICreate(&bodyIndexMap));
1505     PetscCall(PetscHMapICreate(&bodyVertexMap));
1506     PetscCall(PetscHMapICreate(&bodyEdgeMap));
1507     PetscCall(PetscHMapICreate(&bodyEdgeGlobalMap));
1508     PetscCall(PetscHMapICreate(&bodyFaceMap));
1509 
1510     for (b = 0; b < nbodies; ++b) {
1511       ego           body = bodies[b];
1512       int           Nf, Ne, Nv;
1513       PetscHashIter BIiter, BViter, BEiter, BEGiter, BFiter, EMiter;
1514       PetscBool     BIfound, BVfound, BEfound, BEGfound, BFfound, EMfound;
1515 
1516       PetscCall(PetscHMapIFind(bodyIndexMap, b, &BIiter, &BIfound));
1517       PetscCall(PetscHMapIFind(bodyVertexMap, b, &BViter, &BVfound));
1518       PetscCall(PetscHMapIFind(bodyEdgeMap, b, &BEiter, &BEfound));
1519       PetscCall(PetscHMapIFind(bodyEdgeGlobalMap, b, &BEGiter, &BEGfound));
1520       PetscCall(PetscHMapIFind(bodyFaceMap, b, &BFiter, &BFfound));
1521 
1522       if (!BIfound) PetscCall(PetscHMapISet(bodyIndexMap, b, numFaces + numEdges + numVertices));
1523       if (!BVfound) PetscCall(PetscHMapISet(bodyVertexMap, b, numVertices));
1524       if (!BEfound) PetscCall(PetscHMapISet(bodyEdgeMap, b, numEdges));
1525       if (!BEGfound) PetscCall(PetscHMapISet(bodyEdgeGlobalMap, b, edgeCntr));
1526       if (!BFfound) PetscCall(PetscHMapISet(bodyFaceMap, b, numFaces));
1527 
1528       if (islite) {
1529         PetscCall(EGlite_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
1530         PetscCall(EGlite_getBodyTopos(body, NULL, EDGE, &Ne, &eobjs));
1531         PetscCall(EGlite_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
1532         EGlite_free(fobjs);
1533         EGlite_free(eobjs);
1534         EGlite_free(nobjs);
1535       } else {
1536         PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
1537         PetscCall(EG_getBodyTopos(body, NULL, EDGE, &Ne, &eobjs));
1538         PetscCall(EG_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
1539         EG_free(fobjs);
1540         EG_free(eobjs);
1541         EG_free(nobjs);
1542       }
1543 
1544       // Remove DEGENERATE EDGES from Edge count
1545       if (islite) {
1546         PetscCall(EGlite_getBodyTopos(body, NULL, EDGE, &Ne, &eobjs));
1547       } else {
1548         PetscCall(EG_getBodyTopos(body, NULL, EDGE, &Ne, &eobjs));
1549       }
1550 
1551       int Netemp = 0;
1552       for (int e = 0; e < Ne; ++e) {
1553         ego edge = eobjs[e];
1554         int eid;
1555 
1556         if (islite) {
1557           PetscCall(EGlite_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
1558           eid = EGlite_indexBodyTopo(body, edge);
1559         } else {
1560           PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
1561           eid = EG_indexBodyTopo(body, edge);
1562         }
1563 
1564         PetscCall(PetscHMapIFind(edgeMap, edgeCntr + eid - 1, &EMiter, &EMfound));
1565         if (mtype == DEGENERATE) {
1566           if (!EMfound) PetscCall(PetscHMapISet(edgeMap, edgeCntr + eid - 1, -1));
1567         } else {
1568           ++Netemp;
1569           if (!EMfound) PetscCall(PetscHMapISet(edgeMap, edgeCntr + eid - 1, Netemp));
1570         }
1571       }
1572       if (islite) {
1573         EGlite_free(eobjs);
1574       } else {
1575         EG_free(eobjs);
1576       }
1577 
1578       // Determine Number of Cells
1579       if (islite) {
1580         PetscCall(EGlite_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
1581       } else {
1582         PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
1583       }
1584 
1585       for (int f = 0; f < Nf; ++f) {
1586         ego face     = fobjs[f];
1587         int edgeTemp = 0;
1588 
1589         if (islite) {
1590           PetscCall(EGlite_getBodyTopos(body, face, EDGE, &Ne, &eobjs));
1591         } else {
1592           PetscCall(EG_getBodyTopos(body, face, EDGE, &Ne, &eobjs));
1593         }
1594 
1595         for (int e = 0; e < Ne; ++e) {
1596           ego edge = eobjs[e];
1597 
1598           if (islite) {
1599             PetscCall(EGlite_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
1600           } else {
1601             PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
1602           }
1603           if (mtype != DEGENERATE) ++edgeTemp;
1604         }
1605         numCells += (2 * edgeTemp);
1606         if (islite) {
1607           EGlite_free(eobjs);
1608         } else {
1609           EG_free(eobjs);
1610         }
1611       }
1612       if (islite) {
1613         EGlite_free(fobjs);
1614       } else {
1615         EG_free(fobjs);
1616       }
1617 
1618       numFaces += Nf;
1619       numEdges += Netemp;
1620       numVertices += Nv;
1621       edgeCntr += Ne;
1622     }
1623 
1624     // Set up basic DMPlex parameters
1625     dim        = 2;                                 // Assumes 3D Models :: Need to handle 2D models in the future
1626     cdim       = 3;                                 // Assumes 3D Models :: Need to update to handle 2D models in future
1627     numCorners = 3;                                 // Split Faces into triangles
1628     numPoints  = numVertices + numEdges + numFaces; // total number of coordinate points
1629 
1630     PetscCall(PetscMalloc2(numPoints * cdim, &coords, numCells * numCorners, &cells));
1631 
1632     // Get Vertex Coordinates and Set up Cells
1633     for (b = 0; b < nbodies; ++b) {
1634       ego           body = bodies[b];
1635       int           Nf, Ne, Nv;
1636       PetscInt      bodyVertexIndexStart, bodyEdgeIndexStart, bodyEdgeGlobalIndexStart, bodyFaceIndexStart;
1637       PetscHashIter BViter, BEiter, BEGiter, BFiter, EMiter;
1638       PetscBool     BVfound, BEfound, BEGfound, BFfound, EMfound;
1639 
1640       // Vertices on Current Body
1641       if (islite) {
1642         PetscCall(EGlite_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
1643       } else {
1644         PetscCall(EG_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
1645       }
1646 
1647       PetscCall(PetscHMapIFind(bodyVertexMap, b, &BViter, &BVfound));
1648       PetscCheck(BVfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %" PetscInt_FMT " not found in bodyVertexMap", b);
1649       PetscCall(PetscHMapIGet(bodyVertexMap, b, &bodyVertexIndexStart));
1650 
1651       for (int v = 0; v < Nv; ++v) {
1652         ego    vertex = nobjs[v];
1653         double limits[4];
1654         int    id, dummy;
1655 
1656         if (islite) {
1657           PetscCall(EGlite_getTopology(vertex, &geom, &oclass, &mtype, limits, &dummy, &mobjs, &senses));
1658           id = EGlite_indexBodyTopo(body, vertex);
1659         } else {
1660           PetscCall(EG_getTopology(vertex, &geom, &oclass, &mtype, limits, &dummy, &mobjs, &senses));
1661           id = EG_indexBodyTopo(body, vertex);
1662         }
1663 
1664         coords[(bodyVertexIndexStart + id - 1) * cdim + 0] = limits[0];
1665         coords[(bodyVertexIndexStart + id - 1) * cdim + 1] = limits[1];
1666         coords[(bodyVertexIndexStart + id - 1) * cdim + 2] = limits[2];
1667       }
1668       if (islite) {
1669         EGlite_free(nobjs);
1670       } else {
1671         EG_free(nobjs);
1672       }
1673 
1674       // Edge Midpoint Vertices on Current Body
1675       if (islite) {
1676         PetscCall(EGlite_getBodyTopos(body, NULL, EDGE, &Ne, &eobjs));
1677       } else {
1678         PetscCall(EG_getBodyTopos(body, NULL, EDGE, &Ne, &eobjs));
1679       }
1680 
1681       PetscCall(PetscHMapIFind(bodyEdgeMap, b, &BEiter, &BEfound));
1682       PetscCheck(BEfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %" PetscInt_FMT " not found in bodyEdgeMap", b);
1683       PetscCall(PetscHMapIGet(bodyEdgeMap, b, &bodyEdgeIndexStart));
1684 
1685       PetscCall(PetscHMapIFind(bodyEdgeGlobalMap, b, &BEGiter, &BEGfound));
1686       PetscCheck(BEGfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %" PetscInt_FMT " not found in bodyEdgeGlobalMap", b);
1687       PetscCall(PetscHMapIGet(bodyEdgeGlobalMap, b, &bodyEdgeGlobalIndexStart));
1688 
1689       for (int e = 0; e < Ne; ++e) {
1690         ego    edge = eobjs[e];
1691         double range[2], avgt[1], cntrPnt[9];
1692         int    eid, eOffset;
1693         int    periodic;
1694 
1695         if (islite) {
1696           PetscCall(EGlite_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
1697         } else {
1698           PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
1699         }
1700         if (mtype == DEGENERATE) continue;
1701 
1702         if (islite) {
1703           eid = EGlite_indexBodyTopo(body, edge);
1704         } else {
1705           eid = EG_indexBodyTopo(body, edge);
1706         }
1707         // get relative offset from globalEdgeID Vector
1708         PetscCall(PetscHMapIFind(edgeMap, bodyEdgeGlobalIndexStart + eid - 1, &EMiter, &EMfound));
1709         PetscCheck(EMfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Edge %" PetscInt_FMT " not found in edgeMap", bodyEdgeGlobalIndexStart + eid - 1);
1710         PetscCall(PetscHMapIGet(edgeMap, bodyEdgeGlobalIndexStart + eid - 1, &eOffset));
1711 
1712         if (islite) {
1713           PetscCall(EGlite_getRange(edge, range, &periodic));
1714         } else {
1715           PetscCall(EG_getRange(edge, range, &periodic));
1716         }
1717         avgt[0] = (range[0] + range[1]) / 2.;
1718 
1719         if (islite) {
1720           PetscCall(EGlite_evaluate(edge, avgt, cntrPnt));
1721         } else {
1722           PetscCall(EG_evaluate(edge, avgt, cntrPnt));
1723         }
1724         coords[(numVertices + bodyEdgeIndexStart + eOffset - 1) * cdim + 0] = cntrPnt[0];
1725         coords[(numVertices + bodyEdgeIndexStart + eOffset - 1) * cdim + 1] = cntrPnt[1];
1726         coords[(numVertices + bodyEdgeIndexStart + eOffset - 1) * cdim + 2] = cntrPnt[2];
1727       }
1728       if (islite) {
1729         EGlite_free(eobjs);
1730       } else {
1731         EG_free(eobjs);
1732       }
1733       // Face Midpoint Vertices on Current Body
1734       if (islite) {
1735         PetscCall(EGlite_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
1736       } else {
1737         PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
1738       }
1739       PetscCall(PetscHMapIFind(bodyFaceMap, b, &BFiter, &BFfound));
1740       PetscCheck(BFfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in bodyFaceMap", b);
1741       PetscCall(PetscHMapIGet(bodyFaceMap, b, &bodyFaceIndexStart));
1742 
1743       for (int f = 0; f < Nf; ++f) {
1744         ego    face = fobjs[f];
1745         double range[4], avgUV[2], cntrPnt[18];
1746         int    peri, id;
1747 
1748         if (islite) {
1749           id = EGlite_indexBodyTopo(body, face);
1750           PetscCall(EGlite_getRange(face, range, &peri));
1751         } else {
1752           id = EG_indexBodyTopo(body, face);
1753           PetscCall(EG_getRange(face, range, &peri));
1754         }
1755 
1756         avgUV[0] = (range[0] + range[1]) / 2.;
1757         avgUV[1] = (range[2] + range[3]) / 2.;
1758 
1759         if (islite) {
1760           PetscCall(EGlite_evaluate(face, avgUV, cntrPnt));
1761         } else {
1762           PetscCall(EG_evaluate(face, avgUV, cntrPnt));
1763         }
1764 
1765         coords[(numVertices + numEdges + bodyFaceIndexStart + id - 1) * cdim + 0] = cntrPnt[0];
1766         coords[(numVertices + numEdges + bodyFaceIndexStart + id - 1) * cdim + 1] = cntrPnt[1];
1767         coords[(numVertices + numEdges + bodyFaceIndexStart + id - 1) * cdim + 2] = cntrPnt[2];
1768       }
1769       if (islite) {
1770         EGlite_free(fobjs);
1771       } else {
1772         EG_free(fobjs);
1773       }
1774 
1775       // Define Cells :: Note - This could be incorporated in the Face Midpoint Vertices Loop but was kept separate for clarity
1776       if (islite) {
1777         PetscCall(EGlite_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
1778       } else {
1779         PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
1780       }
1781       for (int f = 0; f < Nf; ++f) {
1782         ego face = fobjs[f];
1783         int fID, midFaceID, midPntID, startID, endID, Nl;
1784 
1785         if (islite) {
1786           fID = EGlite_indexBodyTopo(body, face);
1787         } else {
1788           fID = EG_indexBodyTopo(body, face);
1789         }
1790 
1791         midFaceID = numVertices + numEdges + bodyFaceIndexStart + fID - 1;
1792         // Must Traverse Loop to ensure we have all necessary information like the sense (+/- 1) of the edges.
1793         // TODO :: Only handles single loop faces (No holes). The choices for handling multiloop faces are:
1794         //            1) Use the DMPlexCreateGeomFromFile() with the -dm_plex_geom_with_tess = 1 option.
1795         //               This will use a default EGADS tessellation as an initial surface mesh.
1796         //            2) Create the initial surface mesh via a 2D mesher :: Currently not available (?future?)
1797         //               May I suggest the XXXX as a starting point?
1798 
1799         if (islite) {
1800           PetscCall(EGlite_getTopology(face, &geom, &oclass, &mtype, NULL, &Nl, &lobjs, &lSenses));
1801         } else {
1802           PetscCall(EG_getTopology(face, &geom, &oclass, &mtype, NULL, &Nl, &lobjs, &lSenses));
1803         }
1804 
1805         PetscCheck(Nl == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Face has %" PetscInt_FMT " Loops. Can only handle Faces with 1 Loop. Please use --dm_plex_geom_with_tess = 1 Option", Nl);
1806         for (int l = 0; l < Nl; ++l) {
1807           ego loop = lobjs[l];
1808 
1809           if (islite) {
1810             PetscCall(EGlite_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &eobjs, &eSenses));
1811           } else {
1812             PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &eobjs, &eSenses));
1813           }
1814 
1815           for (int e = 0; e < Ne; ++e) {
1816             ego edge = eobjs[e];
1817             int eid, eOffset;
1818 
1819             if (islite) {
1820               PetscCall(EGlite_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
1821               eid = EGlite_indexBodyTopo(body, edge);
1822             } else {
1823               PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
1824               eid = EG_indexBodyTopo(body, edge);
1825             }
1826             if (mtype == DEGENERATE) continue;
1827 
1828             // get relative offset from globalEdgeID Vector
1829             PetscCall(PetscHMapIFind(edgeMap, bodyEdgeGlobalIndexStart + eid - 1, &EMiter, &EMfound));
1830             PetscCheck(EMfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Edge %" PetscInt_FMT " of Body %" PetscInt_FMT " not found in edgeMap. Global Edge ID :: %" PetscInt_FMT, eid, b, bodyEdgeGlobalIndexStart + eid - 1);
1831             PetscCall(PetscHMapIGet(edgeMap, bodyEdgeGlobalIndexStart + eid - 1, &eOffset));
1832 
1833             midPntID = numVertices + bodyEdgeIndexStart + eOffset - 1;
1834 
1835             if (islite) {
1836               PetscCall(EGlite_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
1837             } else {
1838               PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
1839             }
1840 
1841             if (eSenses[e] > 0) {
1842               if (islite) {
1843                 startID = EGlite_indexBodyTopo(body, nobjs[0]);
1844                 endID   = EGlite_indexBodyTopo(body, nobjs[1]);
1845               } else {
1846                 startID = EG_indexBodyTopo(body, nobjs[0]);
1847                 endID   = EG_indexBodyTopo(body, nobjs[1]);
1848               }
1849             } else {
1850               if (islite) {
1851                 startID = EGlite_indexBodyTopo(body, nobjs[1]);
1852                 endID   = EGlite_indexBodyTopo(body, nobjs[0]);
1853               } else {
1854                 startID = EG_indexBodyTopo(body, nobjs[1]);
1855                 endID   = EG_indexBodyTopo(body, nobjs[0]);
1856               }
1857             }
1858 
1859             // Define 2 Cells per Edge with correct orientation
1860             cells[cellCntr * numCorners + 0] = midFaceID;
1861             cells[cellCntr * numCorners + 1] = bodyVertexIndexStart + startID - 1;
1862             cells[cellCntr * numCorners + 2] = midPntID;
1863 
1864             cells[cellCntr * numCorners + 3] = midFaceID;
1865             cells[cellCntr * numCorners + 4] = midPntID;
1866             cells[cellCntr * numCorners + 5] = bodyVertexIndexStart + endID - 1;
1867 
1868             cellCntr = cellCntr + 2;
1869           }
1870         }
1871       }
1872       if (islite) {
1873         EGlite_free(fobjs);
1874       } else {
1875         EG_free(fobjs);
1876       }
1877     }
1878   }
1879 
1880   // Generate DMPlex
1881   PetscCall(DMPlexCreateFromCellListPetsc(PETSC_COMM_WORLD, dim, numCells, numPoints, numCorners, PETSC_TRUE, cells, cdim, coords, &dm));
1882   PetscCall(PetscFree2(coords, cells));
1883   PetscCall(PetscInfo(dm, " Total Number of Unique Cells    = %" PetscInt_FMT " \n", numCells));
1884   PetscCall(PetscInfo(dm, " Total Number of Unique Vertices = %" PetscInt_FMT " \n", numVertices));
1885 
1886   // Embed EGADS model in DM
1887   {
1888     PetscContainer modelObj, contextObj;
1889 
1890     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &modelObj));
1891     PetscCall(PetscContainerSetPointer(modelObj, model));
1892     if (islite) {
1893       PetscCall(PetscContainerSetCtxDestroy(modelObj, DMPlexEGADSliteDestroy_Private));
1894       PetscCall(PetscObjectCompose((PetscObject)dm, "EGADSlite Model", (PetscObject)modelObj));
1895     } else {
1896       PetscCall(PetscContainerSetCtxDestroy(modelObj, DMPlexEGADSDestroy_Private));
1897       PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Model", (PetscObject)modelObj));
1898     }
1899     PetscCall(PetscContainerDestroy(&modelObj));
1900 
1901     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &contextObj));
1902     PetscCall(PetscContainerSetPointer(contextObj, context));
1903 
1904     if (islite) {
1905       PetscCall(PetscContainerSetCtxDestroy(contextObj, DMPlexEGADSliteClose_Private));
1906       PetscCall(PetscObjectCompose((PetscObject)dm, "EGADSlite Context", (PetscObject)contextObj));
1907     } else {
1908       PetscCall(PetscContainerSetCtxDestroy(contextObj, DMPlexEGADSClose_Private));
1909       PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Context", (PetscObject)contextObj));
1910     }
1911     PetscCall(PetscContainerDestroy(&contextObj));
1912   }
1913   // Label points
1914   PetscInt nStart, nEnd;
1915 
1916   PetscCall(DMCreateLabel(dm, "EGADS Body ID"));
1917   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
1918   PetscCall(DMCreateLabel(dm, "EGADS Face ID"));
1919   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
1920   PetscCall(DMCreateLabel(dm, "EGADS Edge ID"));
1921   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
1922   PetscCall(DMCreateLabel(dm, "EGADS Vertex ID"));
1923   PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));
1924 
1925   PetscCall(DMPlexGetHeightStratum(dm, 2, &nStart, &nEnd));
1926 
1927   cellCntr = 0;
1928   for (b = 0; b < nbodies; ++b) {
1929     ego           body = bodies[b];
1930     int           Nv, Ne, Nf;
1931     PetscInt      bodyVertexIndexStart, bodyEdgeIndexStart, bodyEdgeGlobalIndexStart, bodyFaceIndexStart;
1932     PetscHashIter BViter, BEiter, BEGiter, BFiter, EMiter;
1933     PetscBool     BVfound, BEfound, BEGfound, BFfound, EMfound;
1934 
1935     PetscCall(PetscHMapIFind(bodyVertexMap, b, &BViter, &BVfound));
1936     PetscCheck(BVfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in bodyVertexMap", b);
1937     PetscCall(PetscHMapIGet(bodyVertexMap, b, &bodyVertexIndexStart));
1938 
1939     PetscCall(PetscHMapIFind(bodyEdgeMap, b, &BEiter, &BEfound));
1940     PetscCheck(BEfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in bodyEdgeMap", b);
1941     PetscCall(PetscHMapIGet(bodyEdgeMap, b, &bodyEdgeIndexStart));
1942 
1943     PetscCall(PetscHMapIFind(bodyFaceMap, b, &BFiter, &BFfound));
1944     PetscCheck(BFfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in bodyFaceMap", b);
1945     PetscCall(PetscHMapIGet(bodyFaceMap, b, &bodyFaceIndexStart));
1946 
1947     PetscCall(PetscHMapIFind(bodyEdgeGlobalMap, b, &BEGiter, &BEGfound));
1948     PetscCheck(BEGfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in bodyEdgeGlobalMap", b);
1949     PetscCall(PetscHMapIGet(bodyEdgeGlobalMap, b, &bodyEdgeGlobalIndexStart));
1950 
1951     if (islite) {
1952       PetscCall(EGlite_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
1953     } else {
1954       PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
1955     }
1956 
1957     for (int f = 0; f < Nf; ++f) {
1958       ego face = fobjs[f];
1959       int fID, Nl;
1960 
1961       if (islite) {
1962         fID = EGlite_indexBodyTopo(body, face);
1963         PetscCall(EGlite_getBodyTopos(body, face, LOOP, &Nl, &lobjs));
1964       } else {
1965         fID = EG_indexBodyTopo(body, face);
1966         PetscCall(EG_getBodyTopos(body, face, LOOP, &Nl, &lobjs));
1967       }
1968 
1969       for (int l = 0; l < Nl; ++l) {
1970         ego loop = lobjs[l];
1971         int lid;
1972 
1973         if (islite) {
1974           lid = EGlite_indexBodyTopo(body, loop);
1975         } else {
1976           lid = EG_indexBodyTopo(body, loop);
1977         }
1978 
1979         PetscCheck(Nl == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Loop %" PetscInt_FMT " has %" PetscInt_FMT " > 1 faces, which is not supported", lid, Nf);
1980 
1981         if (islite) {
1982           PetscCall(EGlite_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &eobjs, &eSenses));
1983         } else {
1984           PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &eobjs, &eSenses));
1985         }
1986 
1987         for (int e = 0; e < Ne; ++e) {
1988           ego edge = eobjs[e];
1989           int eid, eOffset;
1990 
1991           // Skip DEGENERATE Edges
1992           if (islite) {
1993             PetscCall(EGlite_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
1994           } else {
1995             PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
1996           }
1997 
1998           if (mtype == DEGENERATE) { continue; }
1999 
2000           if (islite) {
2001             eid = EGlite_indexBodyTopo(body, edge);
2002           } else {
2003             eid = EG_indexBodyTopo(body, edge);
2004           }
2005 
2006           // get relative offset from globalEdgeID Vector
2007           PetscCall(PetscHMapIFind(edgeMap, bodyEdgeGlobalIndexStart + eid - 1, &EMiter, &EMfound));
2008           PetscCheck(EMfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Edge %" PetscInt_FMT " of Body %" PetscInt_FMT " not found in edgeMap. Global Edge ID :: %" PetscInt_FMT, eid, b, bodyEdgeGlobalIndexStart + eid - 1);
2009           PetscCall(PetscHMapIGet(edgeMap, bodyEdgeGlobalIndexStart + eid - 1, &eOffset));
2010 
2011           if (islite) {
2012             PetscCall(EGlite_getBodyTopos(body, edge, NODE, &Nv, &nobjs));
2013           } else {
2014             PetscCall(EG_getBodyTopos(body, edge, NODE, &Nv, &nobjs));
2015           }
2016 
2017           for (int v = 0; v < Nv; ++v) {
2018             ego vertex = nobjs[v];
2019             int vID;
2020 
2021             if (islite) {
2022               vID = EGlite_indexBodyTopo(body, vertex);
2023             } else {
2024               vID = EG_indexBodyTopo(body, vertex);
2025             }
2026 
2027             PetscCall(DMLabelSetValue(bodyLabel, nStart + bodyVertexIndexStart + vID - 1, b));
2028             PetscCall(DMLabelSetValue(vertexLabel, nStart + bodyVertexIndexStart + vID - 1, vID));
2029           }
2030           if (islite) {
2031             EGlite_free(nobjs);
2032           } else {
2033             EG_free(nobjs);
2034           }
2035 
2036           PetscCall(DMLabelSetValue(bodyLabel, nStart + numVertices + bodyEdgeIndexStart + eOffset - 1, b));
2037           PetscCall(DMLabelSetValue(edgeLabel, nStart + numVertices + bodyEdgeIndexStart + eOffset - 1, eid));
2038 
2039           // Define Cell faces
2040           for (int jj = 0; jj < 2; ++jj) {
2041             PetscCall(DMLabelSetValue(bodyLabel, cellCntr, b));
2042             PetscCall(DMLabelSetValue(faceLabel, cellCntr, fID));
2043             PetscCall(DMPlexGetCone(dm, cellCntr, &cone));
2044 
2045             PetscCall(DMLabelSetValue(bodyLabel, cone[0], b));
2046             PetscCall(DMLabelSetValue(faceLabel, cone[0], fID));
2047 
2048             PetscCall(DMLabelSetValue(bodyLabel, cone[1], b));
2049             PetscCall(DMLabelSetValue(edgeLabel, cone[1], eid));
2050 
2051             PetscCall(DMLabelSetValue(bodyLabel, cone[2], b));
2052             PetscCall(DMLabelSetValue(faceLabel, cone[2], fID));
2053 
2054             cellCntr = cellCntr + 1;
2055           }
2056         }
2057       }
2058       if (islite) {
2059         EGlite_free(lobjs);
2060       } else {
2061         EG_free(lobjs);
2062       }
2063 
2064       PetscCall(DMLabelSetValue(bodyLabel, nStart + numVertices + numEdges + bodyFaceIndexStart + fID - 1, b));
2065       PetscCall(DMLabelSetValue(faceLabel, nStart + numVertices + numEdges + bodyFaceIndexStart + fID - 1, fID));
2066     }
2067     if (islite) {
2068       EGlite_free(fobjs);
2069     } else {
2070       EG_free(fobjs);
2071     }
2072   }
2073 
2074   PetscCall(PetscHMapIDestroy(&edgeMap));
2075   PetscCall(PetscHMapIDestroy(&bodyIndexMap));
2076   PetscCall(PetscHMapIDestroy(&bodyVertexMap));
2077   PetscCall(PetscHMapIDestroy(&bodyEdgeMap));
2078   PetscCall(PetscHMapIDestroy(&bodyEdgeGlobalMap));
2079   PetscCall(PetscHMapIDestroy(&bodyFaceMap));
2080 
2081   *newdm = dm;
2082   PetscFunctionReturn(PETSC_SUCCESS);
2083 }
2084 
2085 PetscErrorCode DMPlexCreateGeom_Tess_Internal(MPI_Comm comm, ego context, ego model, DM *newdm, PetscBool islite)
2086 {
2087   /* EGADSlite variables */
2088   ego    geom, *bodies, *fobjs;
2089   int    b, oclass, mtype, nbodies, *senses;
2090   int    totalNumTris = 0, totalNumPoints = 0;
2091   double boundBox[6] = {0., 0., 0., 0., 0., 0.}, tessSize;
2092   /* PETSc variables */
2093   DM              dm;
2094   DMLabel         bodyLabel, faceLabel, edgeLabel, vertexLabel;
2095   PetscHMapI      pointIndexStartMap = NULL, triIndexStartMap = NULL, pTypeLabelMap = NULL, pIndexLabelMap = NULL;
2096   PetscHMapI      pBodyIndexLabelMap = NULL, triFaceIDLabelMap = NULL, triBodyIDLabelMap = NULL;
2097   PetscInt        dim = -1, cdim = -1, numCorners = 0, counter = 0;
2098   PetscInt       *cells  = NULL;
2099   const PetscInt *cone   = NULL;
2100   PetscReal      *coords = NULL;
2101   PetscMPIInt     rank;
2102 
2103   PetscFunctionBeginUser;
2104   PetscCallMPI(MPI_Comm_rank(comm, &rank));
2105   if (rank == 0) {
2106     // ---------------------------------------------------------------------------------------------------
2107     // Generate PETSc DMPlex from EGADSlite created Tessellation of geometry
2108     // ---------------------------------------------------------------------------------------------------
2109 
2110     // Calculate cell and vertex sizes
2111     if (islite) {
2112       PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, &nbodies, &bodies, &senses));
2113     } else {
2114       PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &nbodies, &bodies, &senses));
2115     }
2116 
2117     PetscCall(PetscHMapICreate(&pointIndexStartMap));
2118     PetscCall(PetscHMapICreate(&triIndexStartMap));
2119     PetscCall(PetscHMapICreate(&pTypeLabelMap));
2120     PetscCall(PetscHMapICreate(&pIndexLabelMap));
2121     PetscCall(PetscHMapICreate(&pBodyIndexLabelMap));
2122     PetscCall(PetscHMapICreate(&triFaceIDLabelMap));
2123     PetscCall(PetscHMapICreate(&triBodyIDLabelMap));
2124 
2125     /* Create Tessellation of Bodies */
2126     ego *tessArray;
2127 
2128     PetscCall(PetscMalloc1(nbodies, &tessArray));
2129     for (b = 0; b < nbodies; ++b) {
2130       ego           body      = bodies[b];
2131       double        params[3] = {0.0, 0.0, 0.0}; // Parameters for Tessellation
2132       int           Nf, bodyNumPoints = 0, bodyNumTris = 0;
2133       PetscHashIter PISiter, TISiter;
2134       PetscBool     PISfound, TISfound;
2135 
2136       /* Store Start Index for each Body's Point and Tris */
2137       PetscCall(PetscHMapIFind(pointIndexStartMap, b, &PISiter, &PISfound));
2138       PetscCall(PetscHMapIFind(triIndexStartMap, b, &TISiter, &TISfound));
2139 
2140       if (!PISfound) PetscCall(PetscHMapISet(pointIndexStartMap, b, totalNumPoints));
2141       if (!TISfound) PetscCall(PetscHMapISet(triIndexStartMap, b, totalNumTris));
2142 
2143       /* Calculate Tessellation parameters based on Bounding Box */
2144       /* Get Bounding Box Dimensions of the BODY */
2145       if (islite) {
2146         PetscCall(EGlite_getBoundingBox(body, boundBox));
2147       } else {
2148         PetscCall(EG_getBoundingBox(body, boundBox));
2149       }
2150 
2151       tessSize = boundBox[3] - boundBox[0];
2152       if (tessSize < boundBox[4] - boundBox[1]) tessSize = boundBox[4] - boundBox[1];
2153       if (tessSize < boundBox[5] - boundBox[2]) tessSize = boundBox[5] - boundBox[2];
2154 
2155       // TODO :: May want to give users tessellation parameter options //
2156       params[0] = 0.0250 * tessSize;
2157       params[1] = 0.0075 * tessSize;
2158       params[2] = 15.0;
2159 
2160       if (islite) {
2161         PetscCall(EGlite_makeTessBody(body, params, &tessArray[b]));
2162         PetscCall(EGlite_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
2163       } else {
2164         PetscCall(EG_makeTessBody(body, params, &tessArray[b]));
2165         PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
2166       }
2167 
2168       for (int f = 0; f < Nf; ++f) {
2169         ego           face = fobjs[f];
2170         int           len, fID, ntris;
2171         const int    *ptype, *pindex, *ptris, *ptric;
2172         const double *pxyz, *puv;
2173 
2174         // Get Face ID //
2175         if (islite) {
2176           fID = EGlite_indexBodyTopo(body, face);
2177         } else {
2178           fID = EG_indexBodyTopo(body, face);
2179         }
2180 
2181         // Checkout the Surface Tessellation //
2182         if (islite) {
2183           PetscCall(EGlite_getTessFace(tessArray[b], fID, &len, &pxyz, &puv, &ptype, &pindex, &ntris, &ptris, &ptric));
2184         } else {
2185           PetscCall(EG_getTessFace(tessArray[b], fID, &len, &pxyz, &puv, &ptype, &pindex, &ntris, &ptris, &ptric));
2186         }
2187 
2188         // Determine total number of triangle cells in the tessellation //
2189         bodyNumTris += (int)ntris;
2190 
2191         // Check out the point index and coordinate //
2192         for (int p = 0; p < len; ++p) {
2193           int global;
2194 
2195           if (islite) {
2196             PetscCall(EGlite_localToGlobal(tessArray[b], fID, p + 1, &global));
2197           } else {
2198             PetscCall(EG_localToGlobal(tessArray[b], fID, p + 1, &global));
2199           }
2200 
2201           // Determine the total number of points in the tessellation //
2202           bodyNumPoints = PetscMax(bodyNumPoints, global);
2203         }
2204       }
2205       if (islite) {
2206         EGlite_free(fobjs);
2207       } else {
2208         EG_free(fobjs);
2209       }
2210 
2211       totalNumPoints += bodyNumPoints;
2212       totalNumTris += bodyNumTris;
2213     }
2214 
2215     dim        = 2;
2216     cdim       = 3;
2217     numCorners = 3;
2218 
2219     /* NEED TO DEFINE MATRICES/VECTORS TO STORE GEOM REFERENCE DATA   */
2220     /* Fill in below and use to define DMLabels after DMPlex creation */
2221     PetscCall(PetscMalloc2(totalNumPoints * cdim, &coords, totalNumTris * numCorners, &cells));
2222 
2223     for (b = 0; b < nbodies; ++b) {
2224       ego           body = bodies[b];
2225       int           Nf;
2226       PetscInt      pointIndexStart;
2227       PetscHashIter PISiter;
2228       PetscBool     PISfound;
2229 
2230       PetscCall(PetscHMapIFind(pointIndexStartMap, b, &PISiter, &PISfound));
2231       PetscCheck(PISfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %" PetscInt_FMT " not found in pointIndexStartMap", b);
2232       PetscCall(PetscHMapIGet(pointIndexStartMap, b, &pointIndexStart));
2233 
2234       if (islite) {
2235         PetscCall(EGlite_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
2236       } else {
2237         PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
2238       }
2239 
2240       for (int f = 0; f < Nf; ++f) {
2241         /* Get Face Object */
2242         ego           face = fobjs[f];
2243         int           len, fID, ntris;
2244         const int    *ptype, *pindex, *ptris, *ptric;
2245         const double *pxyz, *puv;
2246 
2247         /* Get Face ID */
2248         if (islite) {
2249           fID = EGlite_indexBodyTopo(body, face);
2250         } else {
2251           fID = EG_indexBodyTopo(body, face);
2252         }
2253 
2254         /* Checkout the Surface Tessellation */
2255         if (islite) {
2256           PetscCall(EGlite_getTessFace(tessArray[b], fID, &len, &pxyz, &puv, &ptype, &pindex, &ntris, &ptris, &ptric));
2257         } else {
2258           PetscCall(EG_getTessFace(tessArray[b], fID, &len, &pxyz, &puv, &ptype, &pindex, &ntris, &ptris, &ptric));
2259         }
2260 
2261         /* Check out the point index and coordinate */
2262         for (int p = 0; p < len; ++p) {
2263           int           global;
2264           PetscHashIter PTLiter, PILiter, PBLiter;
2265           PetscBool     PTLfound, PILfound, PBLfound;
2266 
2267           if (islite) {
2268             PetscCall(EGlite_localToGlobal(tessArray[b], fID, p + 1, &global));
2269           } else {
2270             PetscCall(EG_localToGlobal(tessArray[b], fID, p + 1, &global));
2271           }
2272 
2273           /* Set the coordinates array for DAG */
2274           coords[((global - 1 + pointIndexStart) * 3) + 0] = pxyz[(p * 3) + 0];
2275           coords[((global - 1 + pointIndexStart) * 3) + 1] = pxyz[(p * 3) + 1];
2276           coords[((global - 1 + pointIndexStart) * 3) + 2] = pxyz[(p * 3) + 2];
2277 
2278           /* Store Geometry Label Information for DMLabel assignment later */
2279           PetscCall(PetscHMapIFind(pTypeLabelMap, global - 1 + pointIndexStart, &PTLiter, &PTLfound));
2280           PetscCall(PetscHMapIFind(pIndexLabelMap, global - 1 + pointIndexStart, &PILiter, &PILfound));
2281           PetscCall(PetscHMapIFind(pBodyIndexLabelMap, global - 1 + pointIndexStart, &PBLiter, &PBLfound));
2282 
2283           if (!PTLfound) PetscCall(PetscHMapISet(pTypeLabelMap, global - 1 + pointIndexStart, ptype[p]));
2284           if (!PILfound) PetscCall(PetscHMapISet(pIndexLabelMap, global - 1 + pointIndexStart, pindex[p]));
2285           if (!PBLfound) PetscCall(PetscHMapISet(pBodyIndexLabelMap, global - 1 + pointIndexStart, b));
2286 
2287           if (ptype[p] < 0) PetscCall(PetscHMapISet(pIndexLabelMap, global - 1 + pointIndexStart, fID));
2288         }
2289 
2290         for (int t = 0; t < (int)ntris; ++t) {
2291           int           global, globalA, globalB;
2292           PetscHashIter TFLiter, TBLiter;
2293           PetscBool     TFLfound, TBLfound;
2294 
2295           if (islite) {
2296             PetscCall(EGlite_localToGlobal(tessArray[b], fID, ptris[(t * 3) + 0], &global));
2297           } else {
2298             PetscCall(EG_localToGlobal(tessArray[b], fID, ptris[(t * 3) + 0], &global));
2299           }
2300           cells[(counter * 3) + 0] = global - 1 + pointIndexStart;
2301 
2302           if (islite) {
2303             PetscCall(EGlite_localToGlobal(tessArray[b], fID, ptris[(t * 3) + 1], &globalA));
2304           } else {
2305             PetscCall(EG_localToGlobal(tessArray[b], fID, ptris[(t * 3) + 1], &globalA));
2306           }
2307           cells[(counter * 3) + 1] = globalA - 1 + pointIndexStart;
2308 
2309           if (islite) {
2310             PetscCall(EGlite_localToGlobal(tessArray[b], fID, ptris[(t * 3) + 2], &globalB));
2311           } else {
2312             PetscCall(EG_localToGlobal(tessArray[b], fID, ptris[(t * 3) + 2], &globalB));
2313           }
2314           cells[(counter * 3) + 2] = globalB - 1 + pointIndexStart;
2315 
2316           PetscCall(PetscHMapIFind(triFaceIDLabelMap, counter, &TFLiter, &TFLfound));
2317           PetscCall(PetscHMapIFind(triBodyIDLabelMap, counter, &TBLiter, &TBLfound));
2318 
2319           if (!TFLfound) PetscCall(PetscHMapISet(triFaceIDLabelMap, counter, fID));
2320           if (!TBLfound) PetscCall(PetscHMapISet(triBodyIDLabelMap, counter, b));
2321 
2322           counter += 1;
2323         }
2324       }
2325       if (islite) {
2326         EGlite_free(fobjs);
2327       } else {
2328         EG_free(fobjs);
2329       }
2330     }
2331     PetscCall(PetscFree(tessArray));
2332   }
2333 
2334   //Build DMPlex
2335   PetscCall(DMPlexCreateFromCellListPetsc(PETSC_COMM_WORLD, dim, totalNumTris, totalNumPoints, numCorners, PETSC_TRUE, cells, cdim, coords, &dm));
2336   PetscCall(PetscFree2(coords, cells));
2337 
2338   // Embed EGADS model in DM
2339   {
2340     PetscContainer modelObj, contextObj;
2341 
2342     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &modelObj));
2343     PetscCall(PetscContainerSetPointer(modelObj, model));
2344     if (islite) {
2345       PetscCall(PetscContainerSetCtxDestroy(modelObj, (PetscCtxDestroyFn *)DMPlexEGADSliteDestroy_Private));
2346       PetscCall(PetscObjectCompose((PetscObject)dm, "EGADSlite Model", (PetscObject)modelObj));
2347     } else {
2348       PetscCall(PetscContainerSetCtxDestroy(modelObj, (PetscCtxDestroyFn *)DMPlexEGADSDestroy_Private));
2349       PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Model", (PetscObject)modelObj));
2350     }
2351     PetscCall(PetscContainerDestroy(&modelObj));
2352 
2353     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &contextObj));
2354     PetscCall(PetscContainerSetPointer(contextObj, context));
2355 
2356     if (islite) {
2357       PetscCall(PetscContainerSetCtxDestroy(contextObj, (PetscCtxDestroyFn *)DMPlexEGADSliteClose_Private));
2358       PetscCall(PetscObjectCompose((PetscObject)dm, "EGADSlite Context", (PetscObject)contextObj));
2359     } else {
2360       PetscCall(PetscContainerSetCtxDestroy(contextObj, (PetscCtxDestroyFn *)DMPlexEGADSClose_Private));
2361       PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Context", (PetscObject)contextObj));
2362     }
2363     PetscCall(PetscContainerDestroy(&contextObj));
2364   }
2365 
2366   // Label Points
2367   PetscCall(DMCreateLabel(dm, "EGADS Body ID"));
2368   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
2369   PetscCall(DMCreateLabel(dm, "EGADS Face ID"));
2370   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
2371   PetscCall(DMCreateLabel(dm, "EGADS Edge ID"));
2372   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
2373   PetscCall(DMCreateLabel(dm, "EGADS Vertex ID"));
2374   PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));
2375 
2376   /* Get Number of DAG Nodes at each level */
2377   int fStart, fEnd, eStart, eEnd, nStart, nEnd;
2378 
2379   PetscCall(DMPlexGetHeightStratum(dm, 0, &fStart, &fEnd));
2380   PetscCall(DMPlexGetHeightStratum(dm, 1, &eStart, &eEnd));
2381   PetscCall(DMPlexGetHeightStratum(dm, 2, &nStart, &nEnd));
2382 
2383   /* Set DMLabels for NODES */
2384   for (int n = nStart; n < nEnd; ++n) {
2385     int           pTypeVal, pIndexVal, pBodyVal;
2386     PetscHashIter PTLiter, PILiter, PBLiter;
2387     PetscBool     PTLfound, PILfound, PBLfound;
2388 
2389     //Converted to Hash Tables
2390     PetscCall(PetscHMapIFind(pTypeLabelMap, n - nStart, &PTLiter, &PTLfound));
2391     PetscCheck(PTLfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "DAG Point %" PetscInt_FMT " not found in pTypeLabelMap", n);
2392     PetscCall(PetscHMapIGet(pTypeLabelMap, n - nStart, &pTypeVal));
2393 
2394     PetscCall(PetscHMapIFind(pIndexLabelMap, n - nStart, &PILiter, &PILfound));
2395     PetscCheck(PILfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "DAG Point %" PetscInt_FMT " not found in pIndexLabelMap", n);
2396     PetscCall(PetscHMapIGet(pIndexLabelMap, n - nStart, &pIndexVal));
2397 
2398     PetscCall(PetscHMapIFind(pBodyIndexLabelMap, n - nStart, &PBLiter, &PBLfound));
2399     PetscCheck(PBLfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "DAG Point %" PetscInt_FMT " not found in pBodyLabelMap", n);
2400     PetscCall(PetscHMapIGet(pBodyIndexLabelMap, n - nStart, &pBodyVal));
2401 
2402     PetscCall(DMLabelSetValue(bodyLabel, n, pBodyVal));
2403     if (pTypeVal == 0) PetscCall(DMLabelSetValue(vertexLabel, n, pIndexVal));
2404     if (pTypeVal > 0) PetscCall(DMLabelSetValue(edgeLabel, n, pIndexVal));
2405     if (pTypeVal < 0) PetscCall(DMLabelSetValue(faceLabel, n, pIndexVal));
2406   }
2407 
2408   /* Set DMLabels for Edges - Based on the DMLabels of the EDGE's NODES */
2409   for (int e = eStart; e < eEnd; ++e) {
2410     int bodyID_0, vertexID_0, vertexID_1, edgeID_0, edgeID_1, faceID_0, faceID_1;
2411 
2412     PetscCall(DMPlexGetCone(dm, e, &cone));
2413     PetscCall(DMLabelGetValue(bodyLabel, cone[0], &bodyID_0)); // Do I need to check the other end?
2414     PetscCall(DMLabelGetValue(vertexLabel, cone[0], &vertexID_0));
2415     PetscCall(DMLabelGetValue(vertexLabel, cone[1], &vertexID_1));
2416     PetscCall(DMLabelGetValue(edgeLabel, cone[0], &edgeID_0));
2417     PetscCall(DMLabelGetValue(edgeLabel, cone[1], &edgeID_1));
2418     PetscCall(DMLabelGetValue(faceLabel, cone[0], &faceID_0));
2419     PetscCall(DMLabelGetValue(faceLabel, cone[1], &faceID_1));
2420 
2421     PetscCall(DMLabelSetValue(bodyLabel, e, bodyID_0));
2422 
2423     if (edgeID_0 == edgeID_1) PetscCall(DMLabelSetValue(edgeLabel, e, edgeID_0));
2424     else if (vertexID_0 > 0 && edgeID_1 > 0) PetscCall(DMLabelSetValue(edgeLabel, e, edgeID_1));
2425     else if (vertexID_1 > 0 && edgeID_0 > 0) PetscCall(DMLabelSetValue(edgeLabel, e, edgeID_0));
2426     else { /* Do Nothing */ }
2427   }
2428 
2429   /* Set DMLabels for Cells */
2430   for (int f = fStart; f < fEnd; ++f) {
2431     int           edgeID_0;
2432     PetscInt      triBodyVal, triFaceVal;
2433     PetscHashIter TFLiter, TBLiter;
2434     PetscBool     TFLfound, TBLfound;
2435 
2436     // Convert to Hash Table
2437     PetscCall(PetscHMapIFind(triFaceIDLabelMap, f - fStart, &TFLiter, &TFLfound));
2438     PetscCheck(TFLfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "DAG Point %" PetscInt_FMT " not found in triFaceIDLabelMap", f);
2439     PetscCall(PetscHMapIGet(triFaceIDLabelMap, f - fStart, &triFaceVal));
2440 
2441     PetscCall(PetscHMapIFind(triBodyIDLabelMap, f - fStart, &TBLiter, &TBLfound));
2442     PetscCheck(TBLfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "DAG Point %" PetscInt_FMT " not found in triBodyIDLabelMap", f);
2443     PetscCall(PetscHMapIGet(triBodyIDLabelMap, f - fStart, &triBodyVal));
2444 
2445     PetscCall(DMLabelSetValue(bodyLabel, f, triBodyVal));
2446     PetscCall(DMLabelSetValue(faceLabel, f, triFaceVal));
2447 
2448     /* Finish Labeling previously unlabeled DMPlex Edges - Assumes Triangular Cell (3 Edges Max) */
2449     PetscCall(DMPlexGetCone(dm, f, &cone));
2450 
2451     for (int jj = 0; jj < 3; ++jj) {
2452       PetscCall(DMLabelGetValue(edgeLabel, cone[jj], &edgeID_0));
2453 
2454       if (edgeID_0 < 0) {
2455         PetscCall(DMLabelSetValue(bodyLabel, cone[jj], triBodyVal));
2456         PetscCall(DMLabelSetValue(faceLabel, cone[jj], triFaceVal));
2457       }
2458     }
2459   }
2460 
2461   *newdm = dm;
2462   PetscFunctionReturn(PETSC_SUCCESS);
2463 }
2464 #endif
2465 
2466 /*@C
2467   DMPlexInflateToGeomModelUseXYZ - Snaps the vertex coordinates of a `DMPLEX` object representing the mesh to its geometry if some vertices depart from the model. This usually happens with non-conforming refinement.
2468 
2469   Collective
2470 
2471   Input Parameter:
2472 . dm - The uninflated `DM` object representing the mesh
2473 
2474   Level: intermediate
2475 
2476 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexCreateEGADS()`
2477 @*/
2478 PetscErrorCode DMPlexInflateToGeomModelUseXYZ(DM dm) PeNS
2479 {
2480   // please don't fucking write code like this with #ifdef all of the place!
2481 #if defined(PETSC_HAVE_EGADS)
2482   /* EGADS Variables */
2483   ego    model, geom, body, face, edge, vertex;
2484   ego   *bodies;
2485   int    Nb, oclass, mtype, *senses;
2486   double result[4];
2487   /* PETSc Variables */
2488   DM             cdm;
2489   PetscContainer modelObj;
2490   DMLabel        bodyLabel, faceLabel, edgeLabel, vertexLabel;
2491   Vec            coordinates;
2492   PetscScalar   *coords;
2493   PetscInt       bodyID, faceID, edgeID, vertexID;
2494   PetscInt       cdim, d, vStart, vEnd, v;
2495   PetscBool      islite = PETSC_FALSE;
2496 #endif
2497 
2498   PetscFunctionBegin;
2499 #if defined(PETSC_HAVE_EGADS)
2500   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
2501   if (!modelObj) {
2502     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
2503     islite = PETSC_TRUE;
2504   }
2505   if (!modelObj) PetscFunctionReturn(PETSC_SUCCESS);
2506   PetscCall(DMGetCoordinateDim(dm, &cdim));
2507   PetscCall(DMGetCoordinateDM(dm, &cdm));
2508   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
2509   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
2510   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
2511   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
2512   PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));
2513 
2514   PetscCall(PetscContainerGetPointer(modelObj, (void **)&model));
2515 
2516   if (islite) {
2517     PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
2518   } else {
2519     PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
2520   }
2521 
2522   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
2523   PetscCall(VecGetArrayWrite(coordinates, &coords));
2524   for (v = vStart; v < vEnd; ++v) {
2525     PetscScalar *vcoords;
2526 
2527     PetscCall(DMLabelGetValue(bodyLabel, v, &bodyID));
2528     PetscCall(DMLabelGetValue(faceLabel, v, &faceID));
2529     PetscCall(DMLabelGetValue(edgeLabel, v, &edgeID));
2530     PetscCall(DMLabelGetValue(vertexLabel, v, &vertexID));
2531 
2532     PetscCheck(bodyID < Nb, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Body %" PetscInt_FMT " is not in [0, %" PetscInt_FMT ")", bodyID, Nb);
2533     body = bodies[bodyID];
2534 
2535     PetscCall(DMPlexPointLocalRef(cdm, v, coords, (void *)&vcoords));
2536     if (vertexID > 0) {
2537       if (islite) {
2538         PetscCall(EGlite_objectBodyTopo(body, NODE, vertexID, &vertex));
2539         PetscCall(EGlite_evaluate(vertex, NULL, result));
2540       } else {
2541         PetscCall(EG_objectBodyTopo(body, NODE, vertexID, &vertex));
2542         PetscCall(EG_evaluate(vertex, NULL, result));
2543       }
2544       for (d = 0; d < cdim; ++d) vcoords[d] = result[d];
2545     } else if (edgeID > 0) {
2546       /* Snap to EDGE at nearest location */
2547       double params[1];
2548       if (islite) {
2549         PetscCall(EGlite_objectBodyTopo(body, EDGE, edgeID, &edge));
2550         PetscCall(EGlite_invEvaluate(edge, vcoords, params, result));
2551       } // Get (x,y,z) of nearest point on EDGE
2552       else {
2553         PetscCall(EG_objectBodyTopo(body, EDGE, edgeID, &edge));
2554         PetscCall(EG_invEvaluate(edge, vcoords, params, result));
2555       }
2556       for (d = 0; d < cdim; ++d) vcoords[d] = result[d];
2557     } else if (faceID > 0) {
2558       /* Snap to FACE at nearest location */
2559       double params[2];
2560       if (islite) {
2561         PetscCall(EGlite_objectBodyTopo(body, FACE, faceID, &face));
2562         PetscCall(EGlite_invEvaluate(face, vcoords, params, result));
2563       } // Get (x,y,z) of nearest point on FACE
2564       else {
2565         PetscCall(EG_objectBodyTopo(body, FACE, faceID, &face));
2566         PetscCall(EG_invEvaluate(face, vcoords, params, result));
2567       }
2568       for (d = 0; d < cdim; ++d) vcoords[d] = result[d];
2569     }
2570   }
2571   PetscCall(VecRestoreArrayWrite(coordinates, &coords));
2572   /* Clear out global coordinates */
2573   PetscCall(VecDestroy(&dm->coordinates[0].x));
2574 #endif
2575   PetscFunctionReturn(PETSC_SUCCESS);
2576 }
2577 
2578 #if defined(PETSC_HAVE_EGADS)
2579 // This replaces the model in-place
2580 PetscErrorCode ConvertGeomModelToAllBSplines(PetscBool islite, ego *model) PeNS
2581 {
2582   /* EGADS/EGADSlite Variables */
2583   ego  context = NULL, geom, *bodies, *fobjs;
2584   int  oclass, mtype;
2585   int *senses;
2586   int  Nb, Nf;
2587 
2588   PetscFunctionBegin;
2589   // Get the number of bodies and body objects in the model
2590   if (islite) PetscCallEGADS(EGlite_getTopology, (*model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
2591   else PetscCallEGADS(EG_getTopology, (*model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
2592 
2593   // Get all Faces on the body    <-- Only working with 1 body at the moment.
2594   ego body = bodies[0];
2595   if (islite) PetscCallEGADS(EGlite_getBodyTopos, (body, NULL, FACE, &Nf, &fobjs));
2596   else PetscCallEGADS(EG_getBodyTopos, (body, NULL, FACE, &Nf, &fobjs));
2597   ego newGeom[Nf];
2598   ego newFaces[Nf];
2599 
2600   // Convert the 1st Face to a BSpline Geometry
2601   for (int ii = 0; ii < Nf; ++ii) {
2602     ego     face = fobjs[ii];
2603     ego     gRef, gPrev, gNext, *lobjs;
2604     int     goclass, gmtype, *gpinfo;
2605     int     Nl, *lsenses;
2606     double *gprv;
2607     char   *gClass = (char *)"", *gType = (char *)"";
2608 
2609     /* Shape Optimization is NOT available for EGADSlite geometry files. */
2610     /*     Note :: islite options are left below in case future versions of EGADSlite includes this capability */
2611     PetscCheck(!islite, PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot convert geometric entities to all BSplines for geometries defined by EGADSlite (.egadslite)! Please use another geometry file format STEP, IGES, EGADS or BRep");
2612 
2613     if (islite) {
2614       PetscCallEGADS(EGlite_getTopology, (face, &geom, &oclass, &mtype, NULL, &Nl, &lobjs, &lsenses)); // Get FACES Geometry object (geom_
2615       PetscCallEGADS(EGlite_getGeometry, (geom, &goclass, &gmtype, &gRef, &gpinfo, &gprv));            // Get geometry object info
2616       PetscCallEGADS(EGlite_getInfo, (geom, &goclass, &gmtype, &gRef, &gPrev, &gNext));
2617     } // Get geometry info
2618     else {
2619       PetscCallEGADS(EG_getTopology, (face, &geom, &oclass, &mtype, NULL, &Nl, &lobjs, &lsenses)); // Get FACES Geometry object (geom_
2620       PetscCallEGADS(EG_getGeometry, (geom, &goclass, &gmtype, &gRef, &gpinfo, &gprv));            // Get geometry object info
2621       PetscCallEGADS(EG_getInfo, (geom, &goclass, &gmtype, &gRef, &gPrev, &gNext));
2622     } // Get geometry info
2623 
2624     PetscCall(DMPlex_EGADS_GeomDecode_Internal(goclass, gmtype, &gClass, &gType)); // Decode Geometry integers
2625 
2626     // Convert current FACE to a BSpline Surface
2627     ego     bspline;
2628     ego     bRef, bPrev, bNext;
2629     int     boclass, bmtype, *bpinfo;
2630     double *bprv;
2631     char   *bClass = (char *)"", *bType = (char *)"";
2632 
2633     PetscCallEGADS(EG_convertToBSpline, (face, &bspline)); // Does not have an EGlite_ version
2634 
2635     if (islite) {
2636       PetscCallEGADS(EGlite_getGeometry, (bspline, &boclass, &bmtype, &bRef, &bpinfo, &bprv)); // Get geometry object info
2637       PetscCallEGADS(EGlite_getInfo, (bspline, &boclass, &bmtype, &bRef, &bPrev, &bNext));
2638     } // Get geometry info
2639     else {
2640       PetscCallEGADS(EG_getGeometry, (bspline, &boclass, &bmtype, &bRef, &bpinfo, &bprv)); // Get geometry object info
2641       PetscCallEGADS(EG_getInfo, (bspline, &boclass, &bmtype, &bRef, &bPrev, &bNext));
2642     } // Get geometry info
2643 
2644     PetscCall(DMPlex_EGADS_GeomDecode_Internal(boclass, bmtype, &bClass, &bType)); // Decode Geometry integers
2645 
2646     // Get Context from FACE
2647     context = NULL;
2648     PetscCallEGADS(EG_getContext, (face, &context)); // Does not have an EGlite_ version
2649 
2650     // Silence WARNING Regarding OPENCASCADE 7.5
2651     if (islite) PetscCallEGADS(EGlite_setOutLevel, (context, 0));
2652     else PetscCallEGADS(EG_setOutLevel, (context, 0));
2653 
2654     ego newgeom;
2655     PetscCallEGADS(EG_makeGeometry, (context, SURFACE, BSPLINE, NULL, bpinfo, bprv, &newgeom)); // Does not have an EGlite_ version
2656 
2657     PetscCallEGADS(EG_deleteObject, (bspline));
2658 
2659     // Create new FACE based on new SURFACE geometry
2660     double data[4];
2661     int    periodic;
2662     if (islite) PetscCallEGADS(EGlite_getRange, (newgeom, data, &periodic));
2663     else PetscCallEGADS(EG_getRange, (newgeom, data, &periodic));
2664 
2665     ego newface;
2666     PetscCallEGADS(EG_makeFace, (newgeom, SFORWARD, data, &newface)); // Does not have an EGlite_ version
2667     //PetscCallEGADS(EG_deleteObject, (newgeom));
2668     //PetscCallEGADS(EG_deleteObject, (newface));
2669     newFaces[ii] = newface;
2670     newGeom[ii]  = newgeom;
2671 
2672     // Reinstate WARNING Regarding OPENCASCADE 7.5
2673     if (islite) PetscCallEGADS(EGlite_setOutLevel, (context, 1));
2674     else PetscCallEGADS(EG_setOutLevel, (context, 1));
2675   }
2676 
2677   // Sew New Faces together to get a new model
2678   ego newmodel;
2679   PetscCallEGADS(EG_sewFaces, (Nf, newFaces, 0.0, 0, &newmodel)); // Does not have an EGlite_ version
2680   for (int ii = 0; ii < Nf; ++ii) {
2681     PetscCallEGADS(EG_deleteObject, (newFaces[ii]));
2682     PetscCallEGADS(EG_deleteObject, (newGeom[ii]));
2683   }
2684   PetscCallEGADS(EG_deleteObject, (*model));
2685   *model = newmodel;
2686   PetscFunctionReturn(PETSC_SUCCESS);
2687 }
2688 #endif
2689 
2690 /*@C
2691   DMPlexCreateGeomFromFile - Create a `DMPLEX` mesh from an EGADS, IGES, or STEP file.
2692 
2693   Collective
2694 
2695   Input Parameters:
2696 + comm     - The MPI communicator
2697 . filename - The name of the EGADS, IGES, or STEP file
2698 - islite   - Flag for EGADSlite support
2699 
2700   Output Parameter:
2701 . dm - The `DM` object representing the mesh
2702 
2703   Level: beginner
2704 
2705 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexCreateEGADS()`, `DMPlexCreateEGADSliteFromFile()`
2706 @*/
2707 PetscErrorCode DMPlexCreateGeomFromFile(MPI_Comm comm, const char filename[], DM *dm, PetscBool islite) PeNS
2708 {
2709   /* PETSc Variables */
2710   PetscMPIInt rank;
2711   PetscBool   printModel = PETSC_FALSE, tessModel = PETSC_FALSE, newModel = PETSC_FALSE;
2712   PetscBool   shapeOpt = PETSC_FALSE;
2713 
2714 #if defined(PETSC_HAVE_EGADS)
2715   ego context = NULL, model = NULL;
2716 #endif
2717 
2718   PetscFunctionBegin;
2719   PetscAssertPointer(filename, 2);
2720   PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_geom_print_model", &printModel, NULL));
2721   PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_geom_tess_model", &tessModel, NULL));
2722   PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_geom_new_model", &newModel, NULL));
2723   PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_geom_shape_opt", &shapeOpt, NULL));
2724   PetscCallMPI(MPI_Comm_rank(comm, &rank));
2725 #if defined(PETSC_HAVE_EGADS)
2726   if (rank == 0) {
2727     /* EGADSlite files cannot be used for Shape Optimization Work. It lacks the ability to make new geometry. */
2728     /* Must use EGADS, STEP, IGES or BRep files to perform this work.                                         */
2729     if (islite) {
2730       PetscCallEGADS(EGlite_open, (&context));
2731       PetscCallEGADS(EGlite_loadModel, (context, 0, filename, &model));
2732       if (shapeOpt) PetscCall(ConvertGeomModelToAllBSplines(islite, &model));
2733       if (printModel) PetscCall(DMPlexGeomPrintModel_Internal(model, islite));
2734     } else {
2735       PetscCallEGADS(EG_open, (&context));
2736       PetscCallEGADS(EG_loadModel, (context, 0, filename, &model));
2737       if (shapeOpt) PetscCall(ConvertGeomModelToAllBSplines(islite, &model));
2738       if (printModel) PetscCall(DMPlexGeomPrintModel_Internal(model, islite));
2739     }
2740   }
2741   if (tessModel) PetscCall(DMPlexCreateGeom_Tess_Internal(comm, context, model, dm, islite));
2742   else if (newModel) PetscCall(DMPlexCreateGeom_Internal(comm, context, model, dm, islite));
2743   else {
2744     PetscCall(DMPlexCreateGeom(comm, context, model, dm, islite));
2745   }
2746   PetscFunctionReturn(PETSC_SUCCESS);
2747 #else
2748   SETERRQ(comm, PETSC_ERR_SUP, "This method requires EGADS support. Reconfigure using --download-egads");
2749 #endif
2750 }
2751 
2752 #if defined(PETSC_HAVE_EGADS)
2753 /*@C
2754   DMPlex_Surface_Grad - Exposes the Geometry's Control Points and Weights and Calculates the Mesh Topology Boundary Nodes Gradient
2755                         with respect the associated geometry's Control Points and Weights.
2756 
2757                         // ----- Depreciated ---- See DMPlexGeomDataAndGrads ------ //
2758 
2759   Collective
2760 
2761   Input Parameters:
2762 . dm      - The DM object representing the mesh with PetscContainer containing an EGADS geometry model
2763 
2764   Output Parameter:
2765 . dm       - The DM object representing the mesh with PetscContainers containing the EGADS geometry model, Array-Hash Table Geometry Control Point Pair, Array-Hash Table Geometry Weights Pair and Matrix-Hash Table Surface Gradient Pair
2766 
2767   Level: intermediate
2768 
2769 .seealso:
2770 @*/
2771 PetscErrorCode DMPlex_Surface_Grad(DM dm)
2772 {
2773   ego            model, geom, *bodies, *fobjs;
2774   PetscContainer modelObj;
2775   int            oclass, mtype, *senses;
2776   int            Nb, Nf;
2777   PetscHMapI     faceCntrlPtRow_Start = NULL, faceCPWeightsRow_Start = NULL;
2778   PetscHMapI     pointSurfGradRow_Start = NULL;
2779   Mat            pointSurfGrad;
2780   IS             faceLabelValues, edgeLabelValues, vertexLabelValues;
2781   PetscInt       faceLabelSize, edgeLabelSize, vertexLabelSize;
2782   PetscBool      islite = PETSC_FALSE;
2783 
2784   PetscFunctionBegin;
2785   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
2786   if (!modelObj) {
2787     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
2788     islite = PETSC_TRUE;
2789     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, " Cannot provide geometric data or associated calculated gradients for geometries defined by EGADSlite (.egadslite)! \n Please use another geometry file format STEP, IGES, EGADS or BRep");
2790   }
2791 
2792   // Get attached EGADS model (pointer)
2793   PetscCall(PetscContainerGetPointer(modelObj, (void **)&model));
2794 
2795   // Get the bodies in the model
2796   if (islite) {
2797     PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
2798   } else {
2799     PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
2800   }
2801 
2802   ego body = bodies[0]; // Only operate on 1st body. Model should only have 1 body.
2803 
2804   // Get the total number of FACEs in the model
2805   if (islite) {
2806     PetscCall(EGlite_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
2807   } else {
2808     PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
2809   }
2810 
2811   // Get the total number of points and IDs in the DMPlex with a "EGADS Face Label"
2812   // This will provide the total number of DMPlex points on the boundary of the geometry
2813   PetscCall(DMGetLabelIdIS(dm, "EGADS Face ID", &faceLabelValues));
2814   PetscCall(DMGetLabelSize(dm, "EGADS Face ID", &faceLabelSize));
2815 
2816   PetscCall(DMGetLabelIdIS(dm, "EGADS Edge ID", &edgeLabelValues));
2817   PetscCall(DMGetLabelSize(dm, "EGADS Edge ID", &edgeLabelSize));
2818 
2819   PetscCall(DMGetLabelIdIS(dm, "EGADS Vertex ID", &vertexLabelValues));
2820   PetscCall(DMGetLabelSize(dm, "EGADS Vertex ID", &vertexLabelSize));
2821 
2822   const PetscInt *faceIndices, *edgeIndices, *vertexIndices;
2823   PetscCall(ISGetIndices(faceLabelValues, &faceIndices));
2824   PetscCall(ISGetIndices(edgeLabelValues, &edgeIndices));
2825   PetscCall(ISGetIndices(vertexLabelValues, &vertexIndices));
2826 
2827   // Get the points associated with each FACE, EDGE and VERTEX label in the DM
2828   PetscInt totalNumPoints = 0;
2829   for (int ii = 0; ii < faceLabelSize; ++ii) {
2830     // Cycle through FACE labels
2831     PetscInt size;
2832     PetscCall(DMGetStratumSize(dm, "EGADS Face ID", faceIndices[ii], &size));
2833     totalNumPoints += size;
2834   }
2835   PetscCall(ISRestoreIndices(faceLabelValues, &faceIndices));
2836   PetscCall(ISDestroy(&faceLabelValues));
2837 
2838   for (int ii = 0; ii < edgeLabelSize; ++ii) {
2839     // Cycle Through EDGE Labels
2840     PetscInt size;
2841     PetscCall(DMGetStratumSize(dm, "EGADS Edge ID", edgeIndices[ii], &size));
2842     totalNumPoints += size;
2843   }
2844   PetscCall(ISRestoreIndices(edgeLabelValues, &edgeIndices));
2845   PetscCall(ISDestroy(&edgeLabelValues));
2846 
2847   for (int ii = 0; ii < vertexLabelSize; ++ii) {
2848     // Cycle Through VERTEX Labels
2849     PetscInt size;
2850     PetscCall(DMGetStratumSize(dm, "EGADS Vertex ID", vertexIndices[ii], &size));
2851     totalNumPoints += size;
2852   }
2853   PetscCall(ISRestoreIndices(vertexLabelValues, &vertexIndices));
2854   PetscCall(ISDestroy(&vertexLabelValues));
2855 
2856   int     maxNumCPs   = 0;
2857   int     totalNumCPs = 0;
2858   ego     bRef, bPrev, bNext, fgeom, *lobjs;
2859   int     id, boclass, bmtype, *bpinfo;
2860   int     foclass, fmtype, Nl, *lsenses;
2861   double *bprv;
2862   double  fdata[4];
2863 
2864   // Create Hash Tables
2865   PetscInt cntr = 0, wcntr = 0;
2866   PetscCall(PetscHMapICreate(&faceCntrlPtRow_Start));
2867   PetscCall(PetscHMapICreate(&faceCPWeightsRow_Start));
2868 
2869   for (int ii = 0; ii < Nf; ++ii) {
2870     // Need to get the maximum number of Control Points defining the FACEs
2871     ego face = fobjs[ii];
2872     int maxNumCPs_temp;
2873 
2874     if (islite) {
2875       id = EGlite_indexBodyTopo(body, face);
2876       PetscCall(EGlite_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
2877       PetscCall(EGlite_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
2878       PetscCall(EGlite_getInfo(fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
2879     } else {
2880       id = EG_indexBodyTopo(body, face);
2881       PetscCall(EG_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
2882       PetscCall(EG_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
2883       PetscCall(EG_getInfo(fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
2884     }
2885 
2886     maxNumCPs_temp = bpinfo[2] * bpinfo[5];
2887     totalNumCPs += bpinfo[2] * bpinfo[5];
2888 
2889     if (maxNumCPs_temp > maxNumCPs) { maxNumCPs = maxNumCPs_temp; }
2890   }
2891 
2892   PetscInt *cpCoordDataLengthPtr, *wDataLengthPtr;
2893   PetscInt  cpCoordDataLength = 3 * totalNumCPs;
2894   PetscInt  wDataLength       = totalNumCPs;
2895   cpCoordDataLengthPtr        = &cpCoordDataLength;
2896   wDataLengthPtr              = &wDataLength;
2897   PetscScalar *cntrlPtCoords, *cntrlPtWeights;
2898   PetscMalloc1(cpCoordDataLength, &cntrlPtCoords);
2899   PetscMalloc1(wDataLength, &cntrlPtWeights);
2900   for (int ii = 0; ii < Nf; ++ii) {
2901     // Need to Populate Control Point Coordinates and Weight Vectors
2902     ego           face = fobjs[ii];
2903     PetscHashIter hashKeyIter, wHashKeyIter;
2904     PetscBool     hashKeyFound, wHashKeyFound;
2905 
2906     if (islite) {
2907       id = EGlite_indexBodyTopo(body, face);
2908       PetscCall(EGlite_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
2909       PetscCall(EGlite_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
2910       PetscCall(EGlite_getInfo(fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
2911     } else {
2912       id = EG_indexBodyTopo(body, face);
2913       PetscCall(EG_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
2914       PetscCall(EG_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
2915       PetscCall(EG_getInfo(fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
2916     }
2917 
2918     // Store Face ID to 1st Row of Control Point Vector
2919     PetscCall(PetscHMapIFind(faceCntrlPtRow_Start, id, &hashKeyIter, &hashKeyFound));
2920 
2921     if (!hashKeyFound) { PetscCall(PetscHMapISet(faceCntrlPtRow_Start, id, cntr)); }
2922 
2923     int offsetCoord = bpinfo[3] + bpinfo[6];
2924     for (int jj = 0; jj < 3 * bpinfo[2] * bpinfo[5]; ++jj) {
2925       cntrlPtCoords[cntr] = bprv[offsetCoord + jj];
2926       cntr += 1;
2927     }
2928 
2929     // Store Face ID to 1st Row of Control Point Weight Vector
2930     PetscCall(PetscHMapIFind(faceCPWeightsRow_Start, id, &wHashKeyIter, &wHashKeyFound));
2931 
2932     if (!wHashKeyFound) { PetscCall(PetscHMapISet(faceCPWeightsRow_Start, id, wcntr)); }
2933 
2934     int offsetWeight = bpinfo[3] + bpinfo[6] + (3 * bpinfo[2] * bpinfo[5]);
2935     for (int jj = 0; jj < bpinfo[2] * bpinfo[5]; ++jj) {
2936       cntrlPtWeights[wcntr] = bprv[offsetWeight + jj];
2937       wcntr += 1;
2938     }
2939   }
2940 
2941   // Attach Control Point and Weight Data to DM
2942   {
2943     PetscContainer cpOrgObj, cpCoordObj, cpCoordLengthObj;
2944     PetscContainer wOrgObj, wValObj, wDataLengthObj;
2945 
2946     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &cpOrgObj));
2947     PetscCall(PetscContainerSetPointer(cpOrgObj, faceCntrlPtRow_Start));
2948     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Hash Table", (PetscObject)cpOrgObj));
2949     PetscCall(PetscContainerDestroy(&cpOrgObj));
2950 
2951     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &cpCoordObj));
2952     PetscCall(PetscContainerSetPointer(cpCoordObj, cntrlPtCoords));
2953     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Coordinates", (PetscObject)cpCoordObj));
2954     PetscCall(PetscContainerDestroy(&cpCoordObj));
2955 
2956     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &cpCoordLengthObj));
2957     PetscCall(PetscContainerSetPointer(cpCoordLengthObj, cpCoordDataLengthPtr));
2958     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Coordinate Data Length", (PetscObject)cpCoordLengthObj));
2959     PetscCall(PetscContainerDestroy(&cpCoordLengthObj));
2960 
2961     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &wOrgObj));
2962     PetscCall(PetscContainerSetPointer(wOrgObj, faceCPWeightsRow_Start));
2963     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weights Hash Table", (PetscObject)wOrgObj));
2964     PetscCall(PetscContainerDestroy(&wOrgObj));
2965 
2966     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &wValObj));
2967     PetscCall(PetscContainerSetPointer(wValObj, cntrlPtWeights));
2968     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weight Data", (PetscObject)wValObj));
2969     PetscCall(PetscContainerDestroy(&wValObj));
2970 
2971     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &wDataLengthObj));
2972     PetscCall(PetscContainerSetPointer(wDataLengthObj, wDataLengthPtr));
2973     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weight Data Length", (PetscObject)wDataLengthObj));
2974     PetscCall(PetscContainerDestroy(&wDataLengthObj));
2975   }
2976 
2977   // Define Matrix to store  Surface Gradient information dx_i/dCPj_i
2978   PetscInt       gcntr   = 0;
2979   const PetscInt rowSize = 3 * maxNumCPs * totalNumPoints;
2980   const PetscInt colSize = 4 * Nf;
2981 
2982   // Create Point Surface Gradient Matrix
2983   MatCreate(PETSC_COMM_WORLD, &pointSurfGrad);
2984   MatSetSizes(pointSurfGrad, PETSC_DECIDE, PETSC_DECIDE, rowSize, colSize);
2985   MatSetType(pointSurfGrad, MATAIJ);
2986   MatSetUp(pointSurfGrad);
2987 
2988   // Create Hash Table to store Point's stare row in surfaceGrad[][]
2989   PetscCall(PetscHMapICreate(&pointSurfGradRow_Start));
2990 
2991   // Get Coordinates for the DMPlex point
2992   DM           cdm;
2993   PetscInt     dE, Nv;
2994   Vec          coordinatesLocal;
2995   PetscScalar *coords = NULL;
2996   PetscCall(DMGetCoordinateDM(dm, &cdm));
2997   PetscCall(DMGetCoordinateDim(dm, &dE));
2998   PetscCall(DMGetCoordinatesLocal(dm, &coordinatesLocal));
2999 
3000   // CYCLE THROUGH FACEs
3001   for (int ii = 0; ii < Nf; ++ii) {
3002     ego             face = fobjs[ii];
3003     ego            *eobjs, *nobjs;
3004     PetscInt        fid, Ne, Nn;
3005     DMLabel         faceLabel, edgeLabel, nodeLabel;
3006     PetscHMapI      currFaceUniquePoints = NULL;
3007     IS              facePoints, edgePoints, nodePoints;
3008     const PetscInt *fIndices, *eIndices, *nIndices;
3009     PetscInt        fSize, eSize, nSize;
3010     PetscHashIter   fHashKeyIter, eHashKeyIter, nHashKeyIter, pHashKeyIter;
3011     PetscBool       fHashKeyFound, eHashKeyFound, nHashKeyFound, pHashKeyFound;
3012     PetscInt        cfCntr = 0;
3013 
3014     // Get Geometry Object for the Current FACE
3015     if (islite) {
3016       PetscCall(EGlite_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
3017       PetscCall(EGlite_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
3018     } else {
3019       PetscCall(EG_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
3020       PetscCall(EG_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
3021     }
3022 
3023     // Get all EDGE and NODE objects attached to the current FACE
3024     if (islite) {
3025       PetscCall(EGlite_getBodyTopos(body, face, EDGE, &Ne, &eobjs));
3026       PetscCall(EGlite_getBodyTopos(body, face, NODE, &Nn, &nobjs));
3027     } else {
3028       PetscCall(EG_getBodyTopos(body, face, EDGE, &Ne, &eobjs));
3029       PetscCall(EG_getBodyTopos(body, face, NODE, &Nn, &nobjs));
3030     }
3031 
3032     // Get all DMPlex Points that have DMLabel "EGADS Face ID" and store them in a Hash Table for later use
3033     if (islite) {
3034       fid = EGlite_indexBodyTopo(body, face);
3035     } else {
3036       fid = EG_indexBodyTopo(body, face);
3037     }
3038 
3039     PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
3040     PetscCall(DMLabelGetStratumIS(faceLabel, fid, &facePoints));
3041     PetscCall(ISGetIndices(facePoints, &fIndices));
3042     PetscCall(ISGetSize(facePoints, &fSize));
3043 
3044     PetscCall(PetscHMapICreate(&currFaceUniquePoints));
3045 
3046     for (int jj = 0; jj < fSize; ++jj) {
3047       PetscCall(PetscHMapIFind(currFaceUniquePoints, fIndices[jj], &fHashKeyIter, &fHashKeyFound));
3048 
3049       if (!fHashKeyFound) {
3050         PetscCall(PetscHMapISet(currFaceUniquePoints, fIndices[jj], cfCntr));
3051         cfCntr += 1;
3052       }
3053 
3054       PetscCall(PetscHMapIFind(pointSurfGradRow_Start, fIndices[jj], &pHashKeyIter, &pHashKeyFound));
3055 
3056       if (!pHashKeyFound) {
3057         PetscCall(PetscHMapISet(pointSurfGradRow_Start, fIndices[jj], gcntr));
3058         gcntr += 3 * maxNumCPs;
3059       }
3060     }
3061     PetscCall(ISRestoreIndices(facePoints, &fIndices));
3062     PetscCall(ISDestroy(&facePoints));
3063 
3064     // Get all DMPlex Points that have DMLable "EGADS Edge ID" attached to the current FACE and store them in a Hash Table for later use.
3065     for (int jj = 0; jj < Ne; ++jj) {
3066       ego       edge = eobjs[jj];
3067       PetscBool containLabelValue;
3068 
3069       if (islite) {
3070         id = EGlite_indexBodyTopo(body, edge);
3071       } else {
3072         id = EG_indexBodyTopo(body, edge);
3073       }
3074 
3075       PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
3076       PetscCall(DMLabelHasValue(edgeLabel, id, &containLabelValue));
3077 
3078       if (containLabelValue) {
3079         PetscCall(DMLabelGetStratumIS(edgeLabel, id, &edgePoints));
3080         PetscCall(ISGetIndices(edgePoints, &eIndices));
3081         PetscCall(ISGetSize(edgePoints, &eSize));
3082 
3083         for (int kk = 0; kk < eSize; ++kk) {
3084           PetscCall(PetscHMapIFind(currFaceUniquePoints, eIndices[kk], &eHashKeyIter, &eHashKeyFound));
3085 
3086           if (!eHashKeyFound) {
3087             PetscCall(PetscHMapISet(currFaceUniquePoints, eIndices[kk], cfCntr));
3088             cfCntr += 1;
3089           }
3090 
3091           PetscCall(PetscHMapIFind(pointSurfGradRow_Start, eIndices[kk], &pHashKeyIter, &pHashKeyFound));
3092 
3093           if (!pHashKeyFound) {
3094             PetscCall(PetscHMapISet(pointSurfGradRow_Start, eIndices[kk], gcntr));
3095             gcntr += 3 * maxNumCPs;
3096           }
3097         }
3098         PetscCall(ISRestoreIndices(edgePoints, &eIndices));
3099         PetscCall(ISDestroy(&edgePoints));
3100       }
3101     }
3102 
3103     // Get all DMPlex Points that have DMLabel "EGADS Vertex ID" attached to the current FACE and store them in a Hash Table for later use.
3104     for (int jj = 0; jj < Nn; ++jj) {
3105       ego node = nobjs[jj];
3106 
3107       if (islite) {
3108         id = EGlite_indexBodyTopo(body, node);
3109       } else {
3110         id = EG_indexBodyTopo(body, node);
3111       }
3112 
3113       PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &nodeLabel));
3114       PetscCall(DMLabelGetStratumIS(nodeLabel, id, &nodePoints));
3115       PetscCall(ISGetIndices(nodePoints, &nIndices));
3116       PetscCall(ISGetSize(nodePoints, &nSize));
3117 
3118       for (int kk = 0; kk < nSize; ++kk) {
3119         PetscCall(PetscHMapIFind(currFaceUniquePoints, nIndices[kk], &nHashKeyIter, &nHashKeyFound));
3120 
3121         if (!nHashKeyFound) {
3122           PetscCall(PetscHMapISet(currFaceUniquePoints, nIndices[kk], cfCntr));
3123           cfCntr += 1;
3124         }
3125 
3126         PetscCall(PetscHMapIFind(pointSurfGradRow_Start, nIndices[kk], &pHashKeyIter, &pHashKeyFound));
3127         if (!pHashKeyFound) {
3128           PetscCall(PetscHMapISet(pointSurfGradRow_Start, nIndices[kk], gcntr));
3129           gcntr += 3 * maxNumCPs;
3130         }
3131       }
3132       PetscCall(ISRestoreIndices(nodePoints, &nIndices));
3133       PetscCall(ISDestroy(&nodePoints));
3134     }
3135 
3136     // Get the Total Number of entries in the Hash Table
3137     PetscInt currFaceUPSize;
3138     PetscCall(PetscHMapIGetSize(currFaceUniquePoints, &currFaceUPSize));
3139 
3140     // Get Keys
3141     PetscInt currFaceUPKeys[currFaceUPSize], off = 0;
3142     PetscCall(PetscHMapIGetKeys(currFaceUniquePoints, &off, currFaceUPKeys));
3143 
3144     // Cycle through all points on the current FACE
3145     for (int jj = 0; jj < currFaceUPSize; ++jj) {
3146       PetscInt currPointID = currFaceUPKeys[jj];
3147       PetscCall(DMPlexVecGetClosure(cdm, NULL, coordinatesLocal, currPointID, &Nv, &coords));
3148 
3149       // Get UV position of FACE
3150       double params[2], range[4], eval[18];
3151       int    peri;
3152 
3153       if (islite) {
3154         PetscCall(EGlite_getRange(face, range, &peri));
3155       } else {
3156         PetscCall(EG_getRange(face, range, &peri));
3157       }
3158 
3159       PetscCall(DMPlex_Geom_FACE_XYZtoUV_Internal(coords, face, range, 0, dE, params, islite));
3160 
3161       if (islite) {
3162         PetscCall(EGlite_evaluate(face, params, eval));
3163       } else {
3164         PetscCall(EG_evaluate(face, params, eval));
3165       }
3166 
3167       // Make a new SURFACE Geometry by changing the location of the Control Points
3168       int    prvSize = bpinfo[3] + bpinfo[6] + (4 * bpinfo[2] * bpinfo[5]);
3169       double nbprv[prvSize];
3170 
3171       // Cycle through each Control Point
3172       double deltaCoord = 1.0E-4;
3173       int    offset     = bpinfo[3] + bpinfo[6];
3174       int    wOffset    = offset + (3 * bpinfo[2] * bpinfo[5]);
3175       for (int ii = 0; ii < bpinfo[2] * bpinfo[5]; ++ii) {
3176         // Cycle through each direction (x, then y, then z)
3177         for (int kk = 0; kk < 4; ++kk) {
3178           // Reinitialize nbprv[] values because we only want to change one value at a time
3179           for (int mm = 0; mm < prvSize; ++mm) { nbprv[mm] = bprv[mm]; }
3180 
3181           if (kk == 0) { //X
3182             nbprv[offset + 0] = bprv[offset + 0] + deltaCoord;
3183             nbprv[offset + 1] = bprv[offset + 1];
3184             nbprv[offset + 2] = bprv[offset + 2];
3185           } else if (kk == 1) { //Y
3186             nbprv[offset + 0] = bprv[offset + 0];
3187             nbprv[offset + 1] = bprv[offset + 1] + deltaCoord;
3188             nbprv[offset + 2] = bprv[offset + 2];
3189           } else if (kk == 2) { //Z
3190             nbprv[offset + 0] = bprv[offset + 0];
3191             nbprv[offset + 1] = bprv[offset + 1];
3192             nbprv[offset + 2] = bprv[offset + 2] + deltaCoord;
3193           } else if (kk == 3) { // Weights
3194             nbprv[wOffset + ii] = bprv[wOffset + ii] + deltaCoord;
3195           } else {
3196             // currently do nothing
3197           }
3198 
3199           // Create New Surface Based on New Control Points or Weights
3200           ego newgeom, context;
3201           if (islite) {
3202             PetscCall(EGlite_open(&context));
3203             PetscCall(EGlite_setOutLevel(context, 0));
3204           } else {
3205             PetscCall(EG_open(&context));
3206             PetscCall(EG_setOutLevel(context, 0));
3207           }
3208 
3209           PetscCall(EG_makeGeometry(context, SURFACE, BSPLINE, NULL, bpinfo, nbprv, &newgeom)); // Does not have an EGlite_ version KNOWN_ISSUE
3210 
3211           if (islite) {
3212             PetscCall(EGlite_setOutLevel(context, 1));
3213           } else {
3214             PetscCall(EG_setOutLevel(context, 1));
3215           }
3216 
3217           // Evaluate new (x, y, z) Point Position based on new Surface Definition
3218           double newCoords[18];
3219           if (islite) {
3220             PetscCall(EGlite_getRange(newgeom, range, &peri));
3221           } else {
3222             PetscCall(EG_getRange(newgeom, range, &peri));
3223           }
3224 
3225           PetscCall(DMPlex_Geom_FACE_XYZtoUV_Internal(coords, newgeom, range, 0, dE, params, islite));
3226 
3227           if (islite) {
3228             PetscCall(EGlite_evaluate(newgeom, params, newCoords));
3229           } else {
3230             PetscCall(EG_evaluate(newgeom, params, newCoords));
3231           }
3232 
3233           // Now Calculate the Surface Gradient for the change in x-component Control Point
3234           PetscScalar dxdCx = (newCoords[0] - coords[0]) / deltaCoord;
3235           PetscScalar dxdCy = (newCoords[1] - coords[1]) / deltaCoord;
3236           PetscScalar dxdCz = (newCoords[2] - coords[2]) / deltaCoord;
3237 
3238           // Store Gradient Information in surfaceGrad[][] Matrix
3239           PetscInt startRow;
3240           PetscCall(PetscHMapIGet(pointSurfGradRow_Start, currPointID, &startRow));
3241 
3242           // Store Results in PETSc Mat
3243           PetscCall(MatSetValue(pointSurfGrad, startRow + (ii * 3) + 0, ((fid - 1) * 4) + kk, dxdCx, INSERT_VALUES));
3244           PetscCall(MatSetValue(pointSurfGrad, startRow + (ii * 3) + 1, ((fid - 1) * 4) + kk, dxdCy, INSERT_VALUES));
3245           PetscCall(MatSetValue(pointSurfGrad, startRow + (ii * 3) + 2, ((fid - 1) * 4) + kk, dxdCz, INSERT_VALUES));
3246         }
3247         offset += 3;
3248       }
3249       PetscCall(DMPlexVecRestoreClosure(cdm, NULL, coordinatesLocal, currPointID, &Nv, &coords));
3250     }
3251   }
3252 
3253   // Assemble Point Surface Grad Matrix
3254   MatAssemblyBegin(pointSurfGrad, MAT_FINAL_ASSEMBLY);
3255   MatAssemblyEnd(pointSurfGrad, MAT_FINAL_ASSEMBLY);
3256 
3257   // Attach Surface Gradient Hash Table and Matrix to DM
3258   {
3259     PetscContainer surfGradOrgObj, surfGradObj;
3260 
3261     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &surfGradOrgObj));
3262     PetscCall(PetscContainerSetPointer(surfGradOrgObj, pointSurfGradRow_Start));
3263     PetscCall(PetscObjectCompose((PetscObject)dm, "Surface Gradient Hash Table", (PetscObject)surfGradOrgObj));
3264     PetscCall(PetscContainerDestroy(&surfGradOrgObj));
3265 
3266     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &surfGradObj));
3267     PetscCall(PetscContainerSetPointer(surfGradObj, pointSurfGrad));
3268     PetscCall(PetscObjectCompose((PetscObject)dm, "Surface Gradient Matrix", (PetscObject)surfGradObj));
3269     PetscCall(PetscContainerDestroy(&surfGradObj));
3270   }
3271   if (islite) EGlite_free(fobjs);
3272   else EG_free(fobjs);
3273   PetscFunctionReturn(PETSC_SUCCESS);
3274 }
3275 
3276 static PetscErrorCode DestroyHashMap(void **p)
3277 {
3278   PetscFunctionBegin;
3279   PetscCall(PetscHMapIDestroy((PetscHMapI *)p));
3280   PetscFunctionReturn(PETSC_SUCCESS);
3281 }
3282 #endif
3283 
3284 /*@C
3285   DMPlexGeomDataAndGrads - Exposes Control Points and Control Point Weights defining the underlying geometry allowing user manipulation of the geometry.
3286 
3287   Collective
3288 
3289   Input Parameters:
3290 + dm           - The DM object representing the mesh with PetscContainer containing an EGADS geometry model
3291 - fullGeomGrad - PetscBool flag. Determines how the Surface Area and Volume Gradients wrt to Control Points and Control Point Weights are calculated.
3292                       PETSC_FALSE :: Surface Area Gradient wrt Control Points and Control Point Weights are calculated using the change in the local
3293                                      FACE changes (not the entire body). Volume Gradients are not calculated. Faster computations.
3294                       PETSC_TRUE  :: Surface Area Gradietn wrt to Control Points and Control Point Weights are calculated using the change observed in
3295                                      the entire solid body. Volume Gradients are calculated. Slower computation due to the need to generate a new solid
3296                                      body geometry for every Control Point and Control Point Weight change.
3297 
3298   Output Parameter:
3299 . dm - The updated DM object representing the mesh with PetscContainers containing the Control Point, Control Point Weight and Gradient Data.
3300 
3301   Level: intermediate
3302 
3303   Note:
3304   Calculates the DM Point location, surface area and volume gradients wrt to Control Point and Control Point Weights using Finite Difference (small perturbation of Control Point coordinates or Control Point Weight value).
3305 
3306 .seealso: `DMPLEX`, `DMCreate()`, `DMPlexCreateGeom()`, `DMPlexModifyEGADSGeomModel()`
3307 @*/
3308 PetscErrorCode DMPlexGeomDataAndGrads(DM dm, PetscBool fullGeomGrad) PeNS
3309 {
3310 #if defined(PETSC_HAVE_EGADS)
3311   /* PETSc Variables */
3312   PetscContainer modelObj;
3313   PetscHMapI     faceCntrlPtRow_Start = NULL, faceCPWeightsRow_Start = NULL;
3314   PetscHMapI     pointSurfGradRow_Start = NULL;
3315   Mat            pointSurfGrad, cpEquiv;
3316   IS             faceLabelValues, edgeLabelValues, vertexLabelValues;
3317   PetscInt       faceLabelSize, edgeLabelSize, vertexLabelSize;
3318   PetscBool      islite = PETSC_FALSE;
3319   /* EGADS Variables */
3320   ego model, geom, *bodies, *fobjs = NULL;
3321   int oclass, mtype, *senses;
3322   int Nb, Nf;
3323 #endif
3324 
3325   PetscFunctionBegin;
3326 #if defined(PETSC_HAVE_EGADS)
3327 
3328   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
3329   if (!modelObj) {
3330     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
3331     PetscCheck(modelObj, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Input DM must have attached EGADS Geometry Model");
3332     islite = PETSC_TRUE;
3333     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot provide geometric data or associated calculated gradients for geometries defined by EGADSlite (.egadslite)!\nPlease use another geometry file format STEP, IGES, EGADS or BRep");
3334   }
3335 
3336   // Get attached EGADS model (pointer)
3337   PetscCall(PetscContainerGetPointer(modelObj, (void **)&model));
3338 
3339   // Get the bodies in the model
3340   if (islite) {
3341     PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
3342   } else {
3343     PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
3344   }
3345 
3346   ego body = bodies[0]; // Only operate on 1st body. Model should only have 1 body.
3347 
3348   // Get the total number of FACEs in the model
3349   if (islite) {
3350     PetscCall(EGlite_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
3351   } else {
3352     PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
3353   }
3354 
3355   // Get the total number of points and IDs in the DMPlex with a "EGADS Face Label"
3356   // This will provide the total number of DMPlex points on the boundary of the geometry
3357   PetscCall(DMGetLabelIdIS(dm, "EGADS Face ID", &faceLabelValues));
3358   PetscCall(DMGetLabelSize(dm, "EGADS Face ID", &faceLabelSize));
3359 
3360   PetscCall(DMGetLabelIdIS(dm, "EGADS Edge ID", &edgeLabelValues));
3361   PetscCall(DMGetLabelSize(dm, "EGADS Edge ID", &edgeLabelSize));
3362 
3363   PetscCall(DMGetLabelIdIS(dm, "EGADS Vertex ID", &vertexLabelValues));
3364   PetscCall(DMGetLabelSize(dm, "EGADS Vertex ID", &vertexLabelSize));
3365 
3366   const PetscInt *faceIndices, *edgeIndices, *vertexIndices;
3367   PetscCall(ISGetIndices(faceLabelValues, &faceIndices));
3368   PetscCall(ISGetIndices(edgeLabelValues, &edgeIndices));
3369   PetscCall(ISGetIndices(vertexLabelValues, &vertexIndices));
3370 
3371   // Get the points associated with each FACE, EDGE and VERTEX label in the DM
3372   PetscInt totalNumPoints = 0;
3373   for (int f = 0; f < faceLabelSize; ++f) {
3374     // Cycle through FACE labels
3375     PetscInt size;
3376     PetscCall(DMGetStratumSize(dm, "EGADS Face ID", faceIndices[f], &size));
3377     totalNumPoints += size;
3378   }
3379   PetscCall(ISRestoreIndices(faceLabelValues, &faceIndices));
3380   PetscCall(ISDestroy(&faceLabelValues));
3381 
3382   for (int e = 0; e < edgeLabelSize; ++e) {
3383     // Cycle Through EDGE Labels
3384     PetscInt size;
3385     PetscCall(DMGetStratumSize(dm, "EGADS Edge ID", edgeIndices[e], &size));
3386     totalNumPoints += size;
3387   }
3388   PetscCall(ISRestoreIndices(edgeLabelValues, &edgeIndices));
3389   PetscCall(ISDestroy(&edgeLabelValues));
3390 
3391   for (int ii = 0; ii < vertexLabelSize; ++ii) {
3392     // Cycle Through VERTEX Labels
3393     PetscInt size;
3394     PetscCall(DMGetStratumSize(dm, "EGADS Vertex ID", vertexIndices[ii], &size));
3395     totalNumPoints += size;
3396   }
3397   PetscCall(ISRestoreIndices(vertexLabelValues, &vertexIndices));
3398   PetscCall(ISDestroy(&vertexLabelValues));
3399 
3400   int     maxNumCPs   = 0;
3401   int     totalNumCPs = 0;
3402   ego     bRef, bPrev, bNext, fgeom, *lobjs;
3403   int     id, boclass, bmtype, *bpinfo;
3404   int     foclass, fmtype, Nl, *lsenses;
3405   double *bprv;
3406   double  fdata[4];
3407 
3408   // Create Hash Tables
3409   PetscInt cntr = 0, wcntr = 0, vcntr = 0;
3410   PetscCall(PetscHMapICreate(&faceCntrlPtRow_Start));
3411   PetscCall(PetscHMapICreate(&faceCPWeightsRow_Start));
3412 
3413   for (int f = 0; f < Nf; ++f) {
3414     // Need to get the maximum number of Control Points defining the FACEs
3415     ego face = fobjs[f];
3416     int maxNumCPs_temp;
3417 
3418     if (islite) {
3419       id = EGlite_indexBodyTopo(body, face);
3420       PetscCall(EGlite_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
3421       PetscCall(EGlite_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
3422       PetscCall(EGlite_getInfo(fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
3423     } else {
3424       id = EG_indexBodyTopo(body, face);
3425       PetscCall(EG_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
3426       PetscCall(EG_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
3427       PetscCall(EG_getInfo(fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
3428     }
3429     maxNumCPs_temp = bpinfo[2] * bpinfo[5];
3430     totalNumCPs += bpinfo[2] * bpinfo[5];
3431 
3432     if (maxNumCPs_temp > maxNumCPs) { maxNumCPs = maxNumCPs_temp; }
3433   }
3434 
3435   PetscInt *cpCoordDataLengthPtr, *wDataLengthPtr;
3436   PetscInt  cpCoordDataLength = 3 * totalNumCPs;
3437   PetscInt  wDataLength       = totalNumCPs;
3438   cpCoordDataLengthPtr        = &cpCoordDataLength;
3439   wDataLengthPtr              = &wDataLength;
3440 
3441   Vec          cntrlPtCoordsVec, cntrlPtWeightsVec;
3442   PetscScalar *cntrlPtCoords, *cntrlPtWeights;
3443   PetscCall(VecCreateSeq(PETSC_COMM_SELF, cpCoordDataLength, &cntrlPtCoordsVec));
3444   PetscCall(VecCreateSeq(PETSC_COMM_SELF, wDataLength, &cntrlPtWeightsVec));
3445 
3446   // For dSA/dCPi
3447   Vec          gradSACPVec, gradSAWVec, gradVCPVec, gradVWVec;
3448   PetscScalar *gradSACP, *gradSAW, *gradVCP, *gradVW;
3449   PetscCall(VecCreateSeq(PETSC_COMM_SELF, cpCoordDataLength, &gradSACPVec));
3450   PetscCall(VecCreateSeq(PETSC_COMM_SELF, wDataLength, &gradSAWVec));
3451   PetscCall(VecCreateSeq(PETSC_COMM_SELF, cpCoordDataLength, &gradVCPVec));
3452   PetscCall(VecCreateSeq(PETSC_COMM_SELF, wDataLength, &gradVWVec));
3453 
3454   // Control Point - Vertex/Edge/Face Relationship
3455   PetscInt *cp_vertex, *cp_edge, *cp_face;
3456   PetscInt *w_vertex, *w_edge, *w_face;
3457   PetscCall(PetscMalloc1(totalNumCPs, &cp_vertex));
3458   PetscCall(PetscMalloc1(totalNumCPs, &cp_edge));
3459   PetscCall(PetscMalloc1(totalNumCPs, &cp_face));
3460   PetscCall(PetscMalloc1(wDataLength, &w_vertex));
3461   PetscCall(PetscMalloc1(wDataLength, &w_edge));
3462   PetscCall(PetscMalloc1(wDataLength, &w_face));
3463 
3464   for (int f = 0; f < Nf; ++f) {
3465     // Need to Populate Control Point Coordinates and Weight Vectors
3466     ego           face = fobjs[f];
3467     ego          *vobjs, *eobjs;
3468     int           offsetCoord, offsetWeight;
3469     PetscInt      Nv, Ne, wRowStart = 0;
3470     PetscHashIter hashKeyIter, wHashKeyIter;
3471     PetscBool     hashKeyFound, wHashKeyFound;
3472 
3473     if (islite) {
3474       id = EGlite_indexBodyTopo(body, face);
3475       PetscCallEGADS(EGlite_getTopology, (face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
3476       PetscCallEGADS(EGlite_getGeometry, (fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
3477       PetscCallEGADS(EGlite_getInfo, (fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
3478       PetscCallEGADS(EGlite_getBodyTopos, (body, face, NODE, &Nv, &vobjs));
3479     } else {
3480       id = EG_indexBodyTopo(body, face);
3481       PetscCallEGADS(EG_getTopology, (face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
3482       PetscCallEGADS(EG_getGeometry, (fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
3483       PetscCallEGADS(EG_getInfo, (fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
3484       PetscCallEGADS(EG_getBodyTopos, (body, face, NODE, &Nv, &vobjs));
3485     }
3486 
3487     // Store Face ID to 1st Row of Control Point Vector
3488     PetscCall(PetscHMapIFind(faceCntrlPtRow_Start, id, &hashKeyIter, &hashKeyFound));
3489 
3490     if (!hashKeyFound) PetscCall(PetscHMapISet(faceCntrlPtRow_Start, id, cntr));
3491 
3492     PetscCall(VecGetArrayWrite(cntrlPtCoordsVec, &cntrlPtCoords));
3493     offsetCoord = bpinfo[3] + bpinfo[6];
3494     for (int jj = 0; jj < 3 * bpinfo[2] * bpinfo[5]; ++jj) {
3495       cntrlPtCoords[cntr] = bprv[offsetCoord + jj];
3496       cntr += 1;
3497     }
3498 
3499     // Store Face ID to 1st Row of Control Point Weight Vector
3500     PetscCall(PetscHMapIFind(faceCPWeightsRow_Start, id, &wHashKeyIter, &wHashKeyFound));
3501 
3502     if (!wHashKeyFound) {
3503       PetscCall(PetscHMapISet(faceCPWeightsRow_Start, id, wcntr));
3504       wRowStart = wcntr;
3505     }
3506 
3507     PetscCall(VecGetArrayWrite(cntrlPtWeightsVec, &cntrlPtWeights));
3508     offsetWeight = bpinfo[3] + bpinfo[6] + (3 * bpinfo[2] * bpinfo[5]);
3509     for (int jj = 0; jj < bpinfo[2] * bpinfo[5]; ++jj) {
3510       cntrlPtWeights[wcntr] = bprv[offsetWeight + jj];
3511       cp_face[wcntr]        = id;
3512       w_face[wcntr]         = id;
3513       wcntr += 1;
3514     }
3515     PetscCall(VecRestoreArrayWrite(cntrlPtWeightsVec, &cntrlPtWeights));
3516 
3517     // Associate Control Points with Vertex IDs
3518     PetscScalar xcp, ycp, zcp;
3519     offsetCoord = bpinfo[3] + bpinfo[6];
3520     for (int jj = 0; jj < 3 * bpinfo[2] * bpinfo[5]; jj += 3) {
3521       xcp = bprv[offsetCoord + jj + 0];
3522       ycp = bprv[offsetCoord + jj + 1];
3523       zcp = bprv[offsetCoord + jj + 2];
3524 
3525       //Initialize Control Point and Weight to Vertex ID relationship to -1
3526       cp_vertex[vcntr] = -1;
3527       w_vertex[vcntr]  = -1;
3528       cp_edge[vcntr]   = -1;
3529       w_edge[vcntr]    = -1;
3530 
3531       for (int kk = 0; kk < Nv; ++kk) {
3532         int         vid;
3533         double      vCoords[3];
3534         PetscScalar vDelta;
3535         ego         vertex = vobjs[kk];
3536 
3537         if (islite) {
3538           vid = EGlite_indexBodyTopo(body, vertex);
3539           PetscCallEGADS(EGlite_evaluate, (vertex, NULL, vCoords));
3540         } else {
3541           vid = EG_indexBodyTopo(body, vertex);
3542           PetscCallEGADS(EG_evaluate, (vertex, NULL, vCoords));
3543         }
3544         vDelta = PetscSqrtReal(PetscSqr(vCoords[0] - xcp) + PetscSqr(vCoords[1] - ycp) + PetscSqr(vCoords[2] - zcp));
3545 
3546         if (vDelta < 1.0E-15) {
3547           cp_vertex[vcntr] = vid;
3548           w_vertex[vcntr]  = vid;
3549         }
3550       }
3551       vcntr += 1;
3552     }
3553     // These two line could be replaced with DMPlexFreeGeomObject()
3554     if (islite) EGlite_free(vobjs);
3555     else EG_free(vobjs);
3556 
3557     // Associate Control Points with Edge IDs
3558     if (islite) PetscCallEGADS(EGlite_getBodyTopos, (body, face, EDGE, &Ne, &eobjs));
3559     else PetscCallEGADS(EG_getBodyTopos, (body, face, EDGE, &Ne, &eobjs));
3560 
3561     int cpV1, cpV2;
3562     int minID, maxID;
3563 
3564     // Along vmin axis
3565     minID = wRowStart;
3566     maxID = wRowStart + (bpinfo[2] - 1);
3567     cpV1  = cp_vertex[minID];
3568     cpV2  = cp_vertex[maxID];
3569     for (int jj = 0; jj < Ne; ++jj) {
3570       ego edge = eobjs[jj];
3571       ego egeom, *nobjs;
3572       int eoclass, emtype, Nn, *nsenses;
3573       int n1ID, n2ID, eid;
3574 
3575       if (islite) {
3576         eid = EGlite_indexBodyTopo(body, edge);
3577         PetscCallEGADS(EGlite_getTopology, (edge, &egeom, &eoclass, &emtype, NULL, &Nn, &nobjs, &nsenses));
3578       } else {
3579         eid = EG_indexBodyTopo(body, edge);
3580         PetscCallEGADS(EG_getTopology, (edge, &egeom, &eoclass, &emtype, NULL, &Nn, &nobjs, &nsenses));
3581       }
3582 
3583       if (emtype != DEGENERATE) {
3584         // Get IDs for current Edge's End Vertices
3585         if (islite) {
3586           n1ID = EGlite_indexBodyTopo(body, nobjs[0]);
3587           n2ID = EGlite_indexBodyTopo(body, nobjs[1]);
3588         } else {
3589           n1ID = EG_indexBodyTopo(body, nobjs[0]);
3590           n2ID = EG_indexBodyTopo(body, nobjs[1]);
3591         }
3592 
3593         if ((cpV1 == n1ID || cpV1 == n2ID) && (cpV2 == n1ID || cpV2 == n2ID)) {
3594           for (int kk = minID + 1; kk < maxID; ++kk) {
3595             cp_edge[kk] = eid;
3596             w_edge[kk]  = eid;
3597           }
3598         }
3599       }
3600     }
3601 
3602     // Along vmax axis
3603     minID = wRowStart + (bpinfo[2] * (bpinfo[5] - 1));
3604     maxID = wRowStart + (bpinfo[2] * bpinfo[5] - 1);
3605 
3606     cpV1 = cp_vertex[minID];
3607     cpV2 = cp_vertex[maxID];
3608     for (int jj = 0; jj < Ne; ++jj) {
3609       ego edge = eobjs[jj];
3610       ego egeom, *nobjs;
3611       int eoclass, emtype, Nn, *nsenses;
3612       int n1ID, n2ID, eid;
3613 
3614       if (islite) {
3615         eid = EGlite_indexBodyTopo(body, edge);
3616         PetscCallEGADS(EGlite_getTopology, (edge, &egeom, &eoclass, &emtype, NULL, &Nn, &nobjs, &nsenses));
3617       } else {
3618         eid = EG_indexBodyTopo(body, edge);
3619         PetscCallEGADS(EG_getTopology, (edge, &egeom, &eoclass, &emtype, NULL, &Nn, &nobjs, &nsenses));
3620       }
3621 
3622       if (emtype != DEGENERATE) {
3623         // Get IDs for current Edge's End Vertices
3624         if (islite) {
3625           n1ID = EGlite_indexBodyTopo(body, nobjs[0]);
3626           n2ID = EGlite_indexBodyTopo(body, nobjs[1]);
3627         } else {
3628           n1ID = EG_indexBodyTopo(body, nobjs[0]);
3629           n2ID = EG_indexBodyTopo(body, nobjs[1]);
3630         }
3631 
3632         if ((cpV1 == n1ID || cpV1 == n2ID) && (cpV2 == n1ID || cpV2 == n2ID)) {
3633           for (int kk = minID + 1; kk < maxID - 1; ++kk) {
3634             cp_edge[kk] = eid;
3635             w_edge[kk]  = eid;
3636           }
3637         }
3638       }
3639     }
3640 
3641     // Along umin axis
3642     minID = wRowStart;
3643     maxID = wRowStart + (bpinfo[2] * (bpinfo[5] - 1));
3644 
3645     cpV1 = cp_vertex[minID];
3646     cpV2 = cp_vertex[maxID];
3647     for (int jj = 0; jj < Ne; ++jj) {
3648       ego edge = eobjs[jj];
3649       ego egeom, *nobjs;
3650       int eoclass, emtype, Nn, *nsenses;
3651       int n1ID, n2ID, eid;
3652 
3653       if (islite) {
3654         eid = EGlite_indexBodyTopo(body, edge);
3655         PetscCallEGADS(EGlite_getTopology, (edge, &egeom, &eoclass, &emtype, NULL, &Nn, &nobjs, &nsenses));
3656       } else {
3657         eid = EG_indexBodyTopo(body, edge);
3658         PetscCallEGADS(EG_getTopology, (edge, &egeom, &eoclass, &emtype, NULL, &Nn, &nobjs, &nsenses));
3659       }
3660 
3661       if (emtype != DEGENERATE) {
3662         // Get IDs for current Edge's End Vertices
3663         if (islite) {
3664           n1ID = EGlite_indexBodyTopo(body, nobjs[0]);
3665           n2ID = EGlite_indexBodyTopo(body, nobjs[1]);
3666         } else {
3667           n1ID = EG_indexBodyTopo(body, nobjs[0]);
3668           n2ID = EG_indexBodyTopo(body, nobjs[1]);
3669         }
3670 
3671         if ((cpV1 == n1ID || cpV1 == n2ID) && (cpV2 == n1ID || cpV2 == n2ID)) {
3672           for (int kk = minID + bpinfo[2]; kk < maxID; kk += bpinfo[2]) {
3673             cp_edge[kk] = eid;
3674             w_edge[kk]  = eid;
3675           }
3676         }
3677       }
3678     }
3679 
3680     // Along umax axis
3681     minID = wRowStart + (bpinfo[2] - 1);
3682     maxID = wRowStart + (bpinfo[2] * bpinfo[5]) - 1;
3683     cpV1  = cp_vertex[minID];
3684     cpV2  = cp_vertex[maxID];
3685     for (int jj = 0; jj < Ne; ++jj) {
3686       ego edge = eobjs[jj];
3687       ego egeom, *nobjs;
3688       int eoclass, emtype, Nn, *nsenses;
3689       int n1ID, n2ID, eid;
3690 
3691       if (islite) {
3692         eid = EGlite_indexBodyTopo(body, edge);
3693         PetscCallEGADS(EGlite_getTopology, (edge, &egeom, &eoclass, &emtype, NULL, &Nn, &nobjs, &nsenses));
3694       } else {
3695         eid = EG_indexBodyTopo(body, edge);
3696         PetscCallEGADS(EG_getTopology, (edge, &egeom, &eoclass, &emtype, NULL, &Nn, &nobjs, &nsenses));
3697       }
3698 
3699       if (emtype != DEGENERATE) {
3700         // Get IDs for current Edge's End Vertices
3701         if (islite) {
3702           n1ID = EGlite_indexBodyTopo(body, nobjs[0]);
3703           n2ID = EGlite_indexBodyTopo(body, nobjs[1]);
3704         } else {
3705           n1ID = EG_indexBodyTopo(body, nobjs[0]);
3706           n2ID = EG_indexBodyTopo(body, nobjs[1]);
3707         }
3708 
3709         if ((cpV1 == n1ID || cpV1 == n2ID) && (cpV2 == n1ID || cpV2 == n2ID)) {
3710           for (int kk = minID + bpinfo[2]; kk < maxID; kk += bpinfo[2]) {
3711             cp_edge[kk] = eid;
3712             w_edge[kk]  = eid;
3713           }
3714         }
3715       }
3716     }
3717     // These two lines could be replaced with DMPlexFreeGeomObject()
3718     if (islite) EGlite_free(eobjs);
3719     else EG_free(eobjs);
3720   }
3721 
3722   // Determine Control Point Equivalence Matrix relating Control Points between Surfaces
3723   //     Note: The Weights will also be tied together in the same manner
3724   //           Also can use the Weight Hash Table for Row Start ID of each Face
3725   const PetscInt cpRowSize = totalNumCPs;
3726   const PetscInt cpColSize = cpRowSize;
3727   PetscInt      *maxNumRelatePtr;
3728   PetscInt       maxNumRelate = 0;
3729 
3730   // Create Point Surface Gradient Matrix
3731   PetscCall(MatCreate(PETSC_COMM_WORLD, &cpEquiv));
3732   PetscCall(MatSetSizes(cpEquiv, PETSC_DECIDE, PETSC_DECIDE, cpRowSize, cpColSize));
3733   PetscCall(MatSetType(cpEquiv, MATAIJ));
3734   PetscCall(MatSetUp(cpEquiv));
3735 
3736   for (int ii = 0; ii < totalNumCPs; ++ii) {
3737     PetscScalar x1, y1, z1;
3738     PetscInt    maxRelateTemp = 0;
3739 
3740     x1 = cntrlPtCoords[(3 * ii) + 0];
3741     y1 = cntrlPtCoords[(3 * ii) + 1];
3742     z1 = cntrlPtCoords[(3 * ii) + 2];
3743 
3744     for (int jj = 0; jj < totalNumCPs; ++jj) {
3745       PetscScalar x2, y2, z2;
3746       PetscScalar cpDelta, eqFactor;
3747       x2 = cntrlPtCoords[(3 * jj) + 0];
3748       y2 = cntrlPtCoords[(3 * jj) + 1];
3749       z2 = cntrlPtCoords[(3 * jj) + 2];
3750 
3751       cpDelta = PetscSqrtReal(PetscSqr(x2 - x1) + PetscSqr(y2 - y1) + PetscSqr(z2 - z1));
3752       if (cpDelta < 1.0E-15) {
3753         eqFactor = 1.0;
3754         maxRelateTemp += 1;
3755       } else {
3756         eqFactor = 0.0;
3757       }
3758 
3759       // Store Results in PETSc Mat
3760       PetscCall(MatSetValue(cpEquiv, ii, jj, eqFactor, INSERT_VALUES));
3761     }
3762     if (maxRelateTemp > maxNumRelate) maxNumRelate = maxRelateTemp;
3763   }
3764   maxNumRelatePtr = &maxNumRelate;
3765   PetscCall(VecRestoreArrayWrite(cntrlPtCoordsVec, &cntrlPtCoords));
3766 
3767   // Assemble Point Surface Grad Matrix
3768   PetscCall(MatAssemblyBegin(cpEquiv, MAT_FINAL_ASSEMBLY));
3769   PetscCall(MatAssemblyEnd(cpEquiv, MAT_FINAL_ASSEMBLY));
3770 
3771   // Attach Control Point and Weight Data to DM
3772   {
3773     PetscContainer cpOrgObj, cpCoordLengthObj;
3774     PetscContainer wOrgObj, wDataLengthObj;
3775     PetscContainer cp_faceObj, cp_edgeObj, cp_vertexObj;
3776     PetscContainer w_faceObj, w_edgeObj, w_vertexObj;
3777     PetscContainer maxNumRelateObj;
3778 
3779     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Hash Table", (PetscObject *)&cpOrgObj));
3780     if (!cpOrgObj) {
3781       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &cpOrgObj));
3782       PetscCall(PetscContainerSetPointer(cpOrgObj, faceCntrlPtRow_Start));
3783       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Hash Table", (PetscObject)cpOrgObj));
3784       PetscCall(PetscContainerDestroy(&cpOrgObj));
3785     } else {
3786       PetscCall(PetscContainerSetPointer(cpOrgObj, faceCntrlPtRow_Start));
3787     }
3788 
3789     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Coordinates", (PetscObject)cntrlPtCoordsVec));
3790     PetscCall(VecDestroy(&cntrlPtCoordsVec));
3791 
3792     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Coordinate Data Length", (PetscObject *)&cpCoordLengthObj));
3793     if (!cpCoordLengthObj) {
3794       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &cpCoordLengthObj));
3795       PetscCall(PetscContainerSetPointer(cpCoordLengthObj, cpCoordDataLengthPtr));
3796       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Coordinate Data Length", (PetscObject)cpCoordLengthObj));
3797       PetscCall(PetscContainerDestroy(&cpCoordLengthObj));
3798     } else {
3799       PetscCall(PetscContainerSetPointer(cpCoordLengthObj, cpCoordDataLengthPtr));
3800     }
3801 
3802     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weights Hash Table", (PetscObject *)&wOrgObj));
3803     if (!wOrgObj) {
3804       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &wOrgObj));
3805       PetscCall(PetscContainerSetPointer(wOrgObj, faceCPWeightsRow_Start));
3806       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weights Hash Table", (PetscObject)wOrgObj));
3807       PetscCall(PetscContainerDestroy(&wOrgObj));
3808     } else {
3809       PetscCall(PetscContainerSetPointer(wOrgObj, faceCPWeightsRow_Start));
3810     }
3811 
3812     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weight Data", (PetscObject)cntrlPtWeightsVec));
3813     PetscCall(VecDestroy(&cntrlPtWeightsVec));
3814 
3815     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight Data Length", (PetscObject *)&wDataLengthObj));
3816     if (!wDataLengthObj) {
3817       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &wDataLengthObj));
3818       PetscCall(PetscContainerSetPointer(wDataLengthObj, wDataLengthPtr));
3819       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weight Data Length", (PetscObject)wDataLengthObj));
3820       PetscCall(PetscContainerDestroy(&wDataLengthObj));
3821     } else {
3822       PetscCall(PetscContainerSetPointer(wDataLengthObj, wDataLengthPtr));
3823     }
3824 
3825     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Equivalency Matrix", (PetscObject)cpEquiv));
3826 
3827     PetscCall(PetscObjectQuery((PetscObject)dm, "Maximum Number Control Point Equivalency", (PetscObject *)&maxNumRelateObj));
3828     if (!maxNumRelateObj) {
3829       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &maxNumRelateObj));
3830       PetscCall(PetscContainerSetPointer(maxNumRelateObj, maxNumRelatePtr));
3831       PetscCall(PetscObjectCompose((PetscObject)dm, "Maximum Number Control Point Equivalency", (PetscObject)maxNumRelateObj));
3832       PetscCall(PetscContainerDestroy(&maxNumRelateObj));
3833     } else {
3834       PetscCall(PetscContainerSetPointer(maxNumRelateObj, maxNumRelatePtr));
3835     }
3836 
3837     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point - Face Map", (PetscObject *)&cp_faceObj));
3838     if (!cp_faceObj) {
3839       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &cp_faceObj));
3840       PetscCall(PetscContainerSetPointer(cp_faceObj, cp_face));
3841       PetscCall(PetscContainerSetCtxDestroy(cp_faceObj, PetscCtxDestroyDefault));
3842       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point - Face Map", (PetscObject)cp_faceObj));
3843       PetscCall(PetscContainerDestroy(&cp_faceObj));
3844     } else {
3845       void *tmp;
3846 
3847       PetscCall(PetscContainerGetPointer(cp_faceObj, &tmp));
3848       PetscCall(PetscFree(tmp));
3849       PetscCall(PetscContainerSetPointer(cp_faceObj, cp_face));
3850     }
3851 
3852     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight - Face Map", (PetscObject *)&w_faceObj));
3853     if (!w_faceObj) {
3854       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &w_faceObj));
3855       PetscCall(PetscContainerSetPointer(w_faceObj, w_face));
3856       PetscCall(PetscContainerSetCtxDestroy(w_faceObj, PetscCtxDestroyDefault));
3857       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weight - Face Map", (PetscObject)w_faceObj));
3858       PetscCall(PetscContainerDestroy(&w_faceObj));
3859     } else {
3860       void *tmp;
3861 
3862       PetscCall(PetscContainerGetPointer(w_faceObj, &tmp));
3863       PetscCall(PetscFree(tmp));
3864       PetscCall(PetscContainerSetPointer(w_faceObj, w_face));
3865     }
3866 
3867     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point - Edge Map", (PetscObject *)&cp_edgeObj));
3868     if (!cp_edgeObj) {
3869       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &cp_edgeObj));
3870       PetscCall(PetscContainerSetPointer(cp_edgeObj, cp_edge));
3871       PetscCall(PetscContainerSetCtxDestroy(cp_edgeObj, PetscCtxDestroyDefault));
3872       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point - Edge Map", (PetscObject)cp_edgeObj));
3873       PetscCall(PetscContainerDestroy(&cp_edgeObj));
3874     } else {
3875       void *tmp;
3876 
3877       PetscCall(PetscContainerGetPointer(cp_edgeObj, &tmp));
3878       PetscCall(PetscFree(tmp));
3879       PetscCall(PetscContainerSetPointer(cp_edgeObj, cp_edge));
3880     }
3881 
3882     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight - Edge Map", (PetscObject *)&w_edgeObj));
3883     if (!w_edgeObj) {
3884       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &w_edgeObj));
3885       PetscCall(PetscContainerSetPointer(w_edgeObj, w_edge));
3886       PetscCall(PetscContainerSetCtxDestroy(w_edgeObj, PetscCtxDestroyDefault));
3887       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weight - Edge Map", (PetscObject)w_edgeObj));
3888       PetscCall(PetscContainerDestroy(&w_edgeObj));
3889     } else {
3890       void *tmp;
3891 
3892       PetscCall(PetscContainerGetPointer(w_edgeObj, &tmp));
3893       PetscCall(PetscFree(tmp));
3894       PetscCall(PetscContainerSetPointer(w_edgeObj, w_edge));
3895     }
3896 
3897     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point - Vertex Map", (PetscObject *)&cp_vertexObj));
3898     if (!cp_vertexObj) {
3899       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &cp_vertexObj));
3900       PetscCall(PetscContainerSetPointer(cp_vertexObj, cp_vertex));
3901       PetscCall(PetscContainerSetCtxDestroy(cp_vertexObj, PetscCtxDestroyDefault));
3902       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point - Vertex Map", (PetscObject)cp_vertexObj));
3903       PetscCall(PetscContainerDestroy(&cp_vertexObj));
3904     } else {
3905       void *tmp;
3906 
3907       PetscCall(PetscContainerGetPointer(cp_vertexObj, &tmp));
3908       PetscCall(PetscFree(tmp));
3909       PetscCall(PetscContainerSetPointer(cp_vertexObj, cp_vertex));
3910     }
3911 
3912     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight - Vertex Map", (PetscObject *)&w_vertexObj));
3913     if (!w_vertexObj) {
3914       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &w_vertexObj));
3915       PetscCall(PetscContainerSetPointer(w_vertexObj, w_vertex));
3916       PetscCall(PetscContainerSetCtxDestroy(w_vertexObj, PetscCtxDestroyDefault));
3917       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weight - Vertex Map", (PetscObject)w_vertexObj));
3918       PetscCall(PetscContainerDestroy(&w_vertexObj));
3919     } else {
3920       void *tmp;
3921 
3922       PetscCall(PetscContainerGetPointer(w_vertexObj, &tmp));
3923       PetscCall(PetscFree(tmp));
3924       PetscCall(PetscContainerSetPointer(w_vertexObj, w_vertex));
3925     }
3926   }
3927 
3928   // Define Matrix to store  Geometry Gradient information dGeom_i/dCPj_i
3929   PetscInt       gcntr   = 0;
3930   const PetscInt rowSize = 3 * maxNumCPs * totalNumPoints;
3931   const PetscInt colSize = 4 * Nf;
3932 
3933   // Create Point Surface Gradient Matrix
3934   PetscCall(MatCreate(PETSC_COMM_WORLD, &pointSurfGrad));
3935   PetscCall(MatSetSizes(pointSurfGrad, PETSC_DECIDE, PETSC_DECIDE, rowSize, colSize));
3936   PetscCall(MatSetType(pointSurfGrad, MATAIJ));
3937   PetscCall(MatSetUp(pointSurfGrad));
3938 
3939   // Create Hash Table to store Point's stare row in surfaceGrad[][]
3940   PetscCall(PetscHMapICreate(&pointSurfGradRow_Start));
3941 
3942   // Get Coordinates for the DMPlex point
3943   DM           cdm;
3944   PetscInt     dE, Nv;
3945   Vec          coordinatesLocal;
3946   PetscScalar *coords = NULL;
3947 
3948   PetscCall(DMGetCoordinateDM(dm, &cdm));
3949   PetscCall(DMGetCoordinateDim(dm, &dE));
3950   PetscCall(DMGetCoordinatesLocal(dm, &coordinatesLocal));
3951 
3952   // CYCLE THROUGH FACEs
3953   PetscScalar maxGrad = 0.;
3954   PetscCall(VecGetArrayWrite(gradSACPVec, &gradSACP));
3955   PetscCall(VecGetArrayWrite(gradSAWVec, &gradSAW));
3956   PetscCall(VecGetArrayWrite(gradVCPVec, &gradVCP));
3957   PetscCall(VecGetArrayWrite(gradVWVec, &gradVW));
3958   for (int f = 0; f < Nf; ++f) {
3959     ego             face = fobjs[f];
3960     ego            *eobjs, *nobjs;
3961     PetscInt        fid, Ne, Nn;
3962     DMLabel         faceLabel, edgeLabel, nodeLabel;
3963     PetscHMapI      currFaceUniquePoints = NULL;
3964     IS              facePoints, edgePoints, nodePoints;
3965     const PetscInt *fIndices, *eIndices, *nIndices;
3966     PetscInt        fSize, eSize, nSize;
3967     PetscHashIter   fHashKeyIter, eHashKeyIter, nHashKeyIter, pHashKeyIter;
3968     PetscBool       fHashKeyFound, eHashKeyFound, nHashKeyFound, pHashKeyFound;
3969     PetscInt        cfCntr = 0;
3970 
3971     // Get Geometry Object for the Current FACE
3972     if (islite) {
3973       PetscCall(EGlite_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
3974       PetscCall(EGlite_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
3975     } else {
3976       PetscCall(EG_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
3977       PetscCall(EG_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
3978     }
3979 
3980     // Get all EDGE and NODE objects attached to the current FACE
3981     if (islite) {
3982       PetscCall(EGlite_getBodyTopos(body, face, EDGE, &Ne, &eobjs));
3983       PetscCall(EGlite_getBodyTopos(body, face, NODE, &Nn, &nobjs));
3984     } else {
3985       PetscCall(EG_getBodyTopos(body, face, EDGE, &Ne, &eobjs));
3986       PetscCall(EG_getBodyTopos(body, face, NODE, &Nn, &nobjs));
3987     }
3988 
3989     // Get all DMPlex Points that have DMLabel "EGADS Face ID" and store them in a Hash Table for later use
3990     if (islite) {
3991       fid = EGlite_indexBodyTopo(body, face);
3992     } else {
3993       fid = EG_indexBodyTopo(body, face);
3994     }
3995 
3996     PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
3997     PetscCall(DMLabelGetStratumIS(faceLabel, fid, &facePoints));
3998     PetscCall(ISGetIndices(facePoints, &fIndices));
3999     PetscCall(ISGetSize(facePoints, &fSize));
4000 
4001     PetscCall(PetscHMapICreate(&currFaceUniquePoints));
4002 
4003     for (int jj = 0; jj < fSize; ++jj) {
4004       PetscCall(PetscHMapIFind(currFaceUniquePoints, fIndices[jj], &fHashKeyIter, &fHashKeyFound));
4005 
4006       if (!fHashKeyFound) {
4007         PetscCall(PetscHMapISet(currFaceUniquePoints, fIndices[jj], cfCntr));
4008         cfCntr += 1;
4009       }
4010 
4011       PetscCall(PetscHMapIFind(pointSurfGradRow_Start, fIndices[jj], &pHashKeyIter, &pHashKeyFound));
4012 
4013       if (!pHashKeyFound) {
4014         PetscCall(PetscHMapISet(pointSurfGradRow_Start, fIndices[jj], gcntr));
4015         gcntr += 3 * maxNumCPs;
4016       }
4017     }
4018     PetscCall(ISRestoreIndices(facePoints, &fIndices));
4019     PetscCall(ISDestroy(&facePoints));
4020 
4021     // Get all DMPlex Points that have DMLable "EGADS Edge ID" attached to the current FACE and store them in a Hash Table for later use.
4022     for (int jj = 0; jj < Ne; ++jj) {
4023       ego       edge = eobjs[jj];
4024       PetscBool containLabelValue;
4025 
4026       if (islite) {
4027         id = EGlite_indexBodyTopo(body, edge);
4028       } else {
4029         id = EG_indexBodyTopo(body, edge);
4030       }
4031 
4032       PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
4033       PetscCall(DMLabelHasValue(edgeLabel, id, &containLabelValue));
4034 
4035       if (containLabelValue) {
4036         PetscCall(DMLabelGetStratumIS(edgeLabel, id, &edgePoints));
4037         PetscCall(ISGetIndices(edgePoints, &eIndices));
4038         PetscCall(ISGetSize(edgePoints, &eSize));
4039 
4040         for (int kk = 0; kk < eSize; ++kk) {
4041           PetscCall(PetscHMapIFind(currFaceUniquePoints, eIndices[kk], &eHashKeyIter, &eHashKeyFound));
4042 
4043           if (!eHashKeyFound) {
4044             PetscCall(PetscHMapISet(currFaceUniquePoints, eIndices[kk], cfCntr));
4045             cfCntr += 1;
4046           }
4047 
4048           PetscCall(PetscHMapIFind(pointSurfGradRow_Start, eIndices[kk], &pHashKeyIter, &pHashKeyFound));
4049 
4050           if (!pHashKeyFound) {
4051             PetscCall(PetscHMapISet(pointSurfGradRow_Start, eIndices[kk], gcntr));
4052             gcntr += 3 * maxNumCPs;
4053           }
4054         }
4055         PetscCall(ISRestoreIndices(edgePoints, &eIndices));
4056         PetscCall(ISDestroy(&edgePoints));
4057       }
4058     }
4059 
4060     // Get all DMPlex Points that have DMLabel "EGADS Vertex ID" attached to the current FACE and store them in a Hash Table for later use.
4061     for (int jj = 0; jj < Nn; ++jj) {
4062       ego node = nobjs[jj];
4063 
4064       if (islite) {
4065         id = EGlite_indexBodyTopo(body, node);
4066       } else {
4067         id = EG_indexBodyTopo(body, node);
4068       }
4069 
4070       PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &nodeLabel));
4071       PetscCall(DMLabelGetStratumIS(nodeLabel, id, &nodePoints));
4072       PetscCall(ISGetIndices(nodePoints, &nIndices));
4073       PetscCall(ISGetSize(nodePoints, &nSize));
4074 
4075       for (int kk = 0; kk < nSize; ++kk) {
4076         PetscCall(PetscHMapIFind(currFaceUniquePoints, nIndices[kk], &nHashKeyIter, &nHashKeyFound));
4077 
4078         if (!nHashKeyFound) {
4079           PetscCall(PetscHMapISet(currFaceUniquePoints, nIndices[kk], cfCntr));
4080           cfCntr += 1;
4081         }
4082 
4083         PetscCall(PetscHMapIFind(pointSurfGradRow_Start, nIndices[kk], &pHashKeyIter, &pHashKeyFound));
4084         if (!pHashKeyFound) {
4085           PetscCall(PetscHMapISet(pointSurfGradRow_Start, nIndices[kk], gcntr));
4086           gcntr += 3 * maxNumCPs;
4087         }
4088       }
4089       PetscCall(ISRestoreIndices(nodePoints, &nIndices));
4090       PetscCall(ISDestroy(&nodePoints));
4091     }
4092 
4093     // Get the Total Number of entries in the Hash Table
4094     PetscInt currFaceUPSize;
4095     PetscCall(PetscHMapIGetSize(currFaceUniquePoints, &currFaceUPSize));
4096 
4097     // Get Keys
4098     PetscInt currFaceUPKeys[currFaceUPSize], off = 0;
4099     PetscCall(PetscHMapIGetKeys(currFaceUniquePoints, &off, currFaceUPKeys));
4100     PetscCall(PetscHMapIDestroy(&currFaceUniquePoints));
4101 
4102     // Get Current Face Surface Area
4103     PetscScalar fSA, faceData[14];
4104     PetscCall(EG_getMassProperties(face, faceData)); // This doesn't have a EGlite version. Will it work for EGADSlite files??  KNOWN_ISSUE
4105     fSA = faceData[1];
4106 
4107     // Get Start Row in cpEquiv Matrix
4108     PetscHashIter Witer;
4109     PetscBool     Wfound;
4110     PetscInt      faceWStartRow;
4111     PetscCall(PetscHMapIFind(faceCPWeightsRow_Start, fid, &Witer, &Wfound));
4112     PetscCheck(Wfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "FACE ID not found in Control Point Weights Hash Table");
4113     PetscCall(PetscHMapIGet(faceCPWeightsRow_Start, fid, &faceWStartRow));
4114 
4115     // Cycle through all points on the current FACE
4116     for (int jj = 0; jj < currFaceUPSize; ++jj) {
4117       PetscInt currPointID = currFaceUPKeys[jj];
4118       PetscCall(DMPlexVecGetClosure(cdm, NULL, coordinatesLocal, currPointID, &Nv, &coords));
4119 
4120       // Get UV position of FACE
4121       double params[2], range[4], eval[18];
4122       int    peri;
4123 
4124       if (islite) PetscCall(EGlite_getRange(face, range, &peri));
4125       else PetscCall(EG_getRange(face, range, &peri));
4126 
4127       PetscCall(DMPlex_Geom_FACE_XYZtoUV_Internal(coords, face, range, 0, dE, params, islite));
4128 
4129       if (islite) PetscCall(EGlite_evaluate(face, params, eval));
4130       else PetscCall(EG_evaluate(face, params, eval));
4131 
4132       // Make a new SURFACE Geometry by changing the location of the Control Points
4133       int    prvSize = bpinfo[3] + bpinfo[6] + (4 * bpinfo[2] * bpinfo[5]);
4134       double nbprv[prvSize];
4135 
4136       // Cycle through each Control Point
4137       double denomNew, denomOld;
4138       double deltaCoord = 1.0E-4;
4139       int    offset     = bpinfo[3] + bpinfo[6];
4140       int    wOffset    = offset + (3 * bpinfo[2] * bpinfo[5]);
4141       for (int ii = 0; ii < bpinfo[2] * bpinfo[5]; ++ii) {
4142         PetscCheck(face->blind, PETSC_COMM_SELF, PETSC_ERR_LIB, "Face %d is corrupted: %d %d", f, jj, ii);
4143   #if 0
4144         // Cycle through each direction (x, then y, then z)
4145         if (jj == 0) {
4146           // Get the Number Control Points that are the same as the current points
4147           //    We are looking for repeated Control Points
4148           PetscInt commonCPcntr = 0;
4149           for (int mm = 0; mm < bpinfo[2]*bpinfo[5]; ++mm) {
4150             PetscScalar matValue;
4151             PetscCall(MatGetValue(cpEquiv, faceWStartRow + ii, faceWStartRow + mm, &matValue));
4152 
4153             if (matValue > 0.0) commonCPcntr += 1;
4154           }
4155         }
4156   #endif
4157 
4158         for (int kk = 0; kk < 4; ++kk) {
4159           // Reinitialize nbprv[] values because we only want to change one value at a time
4160           for (int mm = 0; mm < prvSize; ++mm) { nbprv[mm] = bprv[mm]; }
4161           PetscCheck(face->blind, PETSC_COMM_SELF, PETSC_ERR_LIB, "Face %d is corrupted: %d %d %d", f, jj, ii, kk);
4162 
4163           if (kk == 0) { //X
4164             nbprv[offset + 0] = bprv[offset + 0] + deltaCoord;
4165             nbprv[offset + 1] = bprv[offset + 1];
4166             nbprv[offset + 2] = bprv[offset + 2];
4167             denomNew          = nbprv[offset + 0];
4168             denomOld          = bprv[offset + 0];
4169           } else if (kk == 1) { //Y
4170             nbprv[offset + 0] = bprv[offset + 0];
4171             nbprv[offset + 1] = bprv[offset + 1] + deltaCoord;
4172             nbprv[offset + 2] = bprv[offset + 2];
4173             denomNew          = nbprv[offset + 1];
4174             denomOld          = bprv[offset + 1];
4175           } else if (kk == 2) { //Z
4176             nbprv[offset + 0] = bprv[offset + 0];
4177             nbprv[offset + 1] = bprv[offset + 1];
4178             nbprv[offset + 2] = bprv[offset + 2] + deltaCoord;
4179             denomNew          = nbprv[offset + 2];
4180             denomOld          = bprv[offset + 2];
4181           } else if (kk == 3) { // Weights
4182             nbprv[wOffset + ii] = bprv[wOffset + ii] + deltaCoord;
4183             denomNew            = nbprv[wOffset + ii];
4184             denomOld            = bprv[wOffset + ii];
4185           } else {
4186             // currently do nothing
4187           }
4188 
4189           // Create New Surface Based on New Control Points or Weights
4190           ego newgeom, context;
4191           PetscCallEGADS(EG_getContext, (face, &context));                                             // This does not have an EGlite_ version KNOWN_ISSUE
4192           PetscCallEGADS(EG_makeGeometry, (context, SURFACE, BSPLINE, NULL, bpinfo, nbprv, &newgeom)); // This does not have an EGlite_ version KNOWN_ISSUE
4193           PetscCheck(face->blind, PETSC_COMM_SELF, PETSC_ERR_LIB, "Face %d is corrupted: %d %d %d", f, jj, ii, kk);
4194 
4195           // Evaluate new (x, y, z) Point Position based on new Surface Definition
4196           double newCoords[18];
4197           if (islite) PetscCall(EGlite_getRange(newgeom, range, &peri));
4198           else PetscCall(EG_getRange(newgeom, range, &peri));
4199 
4200           PetscCall(DMPlex_Geom_FACE_XYZtoUV_Internal(coords, face, range, 0, dE, params, islite));
4201           PetscCheck(face->blind, PETSC_COMM_SELF, PETSC_ERR_LIB, "Face %d is corrupted: %d %d %d", f, jj, ii, kk);
4202 
4203           if (islite) PetscCall(EGlite_evaluate(newgeom, params, newCoords));
4204           else PetscCall(EG_evaluate(newgeom, params, newCoords));
4205 
4206           // Calculate Surface Area Gradients wrt Control Points and Weights using the local discrete FACE only
4207           //      NOTE 1: Will not provide Volume Gradient wrt to Control Points and Weights.
4208           //      NOTE 2: This is faster than below where an entire new solid geometry is created for each
4209           //              Control Point and Weight gradient
4210           if (!fullGeomGrad) {
4211             // Create new FACE based on new SURFACE geometry
4212             if (jj == 0) { // only for 1st DMPlex Point because we only per CP or Weight
4213               double newFaceRange[4];
4214               int    newFacePeri;
4215               if (islite) PetscCall(EGlite_getRange(newgeom, newFaceRange, &newFacePeri));
4216               else PetscCall(EG_getRange(newgeom, newFaceRange, &newFacePeri));
4217 
4218               ego newface;
4219               PetscCallEGADS(EG_makeFace, (newgeom, SFORWARD, newFaceRange, &newface)); // Does not have EGlite version KNOWN_ISSUE
4220               PetscCheck(face->blind, PETSC_COMM_SELF, PETSC_ERR_LIB, "Face %d is corrupted: %d %d %d", f, jj, ii, kk);
4221 
4222               // Get New Face Surface Area
4223               PetscScalar newfSA, newFaceData[14];
4224               PetscCall(EG_getMassProperties(newface, newFaceData)); // Does not have EGlite version KNOWN_ISSUE
4225               newfSA = newFaceData[1];
4226               PetscCallEGADS(EG_deleteObject, (newface));
4227               PetscCheck(face->blind, PETSC_COMM_SELF, PETSC_ERR_LIB, "Face %d is corrupted: %d %d %d", f, jj, ii, kk);
4228 
4229               // Update Control Points
4230               PetscHashIter CPiter, Witer;
4231               PetscBool     CPfound, Wfound;
4232               PetscInt      faceCPStartRow, faceWStartRow;
4233 
4234               PetscScalar dSAdCPi;
4235               dSAdCPi = (newfSA - fSA) / (denomNew - denomOld);
4236 
4237               if (kk < 3) {
4238                 PetscCall(PetscHMapIFind(faceCntrlPtRow_Start, fid, &CPiter, &CPfound));
4239                 PetscCheck(CPfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "FACE ID not found in Control Point Hash Table");
4240                 PetscCall(PetscHMapIGet(faceCntrlPtRow_Start, fid, &faceCPStartRow));
4241 
4242                 gradSACP[faceCPStartRow + (ii * 3) + kk] = dSAdCPi;
4243 
4244                 if (PetscAbsReal(dSAdCPi) > maxGrad) maxGrad = PetscAbsReal(dSAdCPi);
4245 
4246               } else if (kk == 3) {
4247                 PetscCall(PetscHMapIFind(faceCPWeightsRow_Start, fid, &Witer, &Wfound));
4248                 PetscCheck(Wfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "FACE ID not found in Control Point Hash Table");
4249                 PetscCall(PetscHMapIGet(faceCPWeightsRow_Start, fid, &faceWStartRow));
4250 
4251                 gradSAW[faceWStartRow + ii] = dSAdCPi;
4252 
4253               } else {
4254                 // Do Nothing
4255               }
4256             }
4257           }
4258           PetscCallEGADS(EG_deleteObject, (newgeom));
4259 
4260           // Now Calculate the Surface Gradient for the change in x-component Control Point
4261           PetscScalar dxdCx = (newCoords[0] - coords[0]) / deltaCoord;
4262           PetscScalar dxdCy = (newCoords[1] - coords[1]) / deltaCoord;
4263           PetscScalar dxdCz = (newCoords[2] - coords[2]) / deltaCoord;
4264 
4265           // Store Gradient Information in surfaceGrad[][] Matrix
4266           PetscInt startRow;
4267           PetscCall(PetscHMapIGet(pointSurfGradRow_Start, currPointID, &startRow));
4268 
4269           // Store Results in PETSc Mat
4270           PetscCall(MatSetValue(pointSurfGrad, startRow + (ii * 3) + 0, ((fid - 1) * 4) + kk, dxdCx, INSERT_VALUES));
4271           PetscCall(MatSetValue(pointSurfGrad, startRow + (ii * 3) + 1, ((fid - 1) * 4) + kk, dxdCy, INSERT_VALUES));
4272           PetscCall(MatSetValue(pointSurfGrad, startRow + (ii * 3) + 2, ((fid - 1) * 4) + kk, dxdCz, INSERT_VALUES));
4273 
4274           //PetscCallEGADS(EG_deleteObject, (newgeom));
4275           PetscCheck(face->blind, PETSC_COMM_SELF, PETSC_ERR_LIB, "Face is corrupted");
4276         }
4277         offset += 3;
4278       }
4279       PetscCall(DMPlexVecRestoreClosure(cdm, NULL, coordinatesLocal, currPointID, &Nv, &coords));
4280     }
4281   }
4282 
4283   // Assemble Point Surface Grad Matrix
4284   PetscCall(MatAssemblyBegin(pointSurfGrad, MAT_FINAL_ASSEMBLY));
4285   PetscCall(MatAssemblyEnd(pointSurfGrad, MAT_FINAL_ASSEMBLY));
4286 
4287   if (fullGeomGrad) {
4288     // Calculate Surface Area and Volume Control Point and Control Point Weight Gradients
4289     //    Note: This is much slower than above due to a new solid geometry being created for
4290     //          each change in Control Point and Control Point Weight. However, this method
4291     //          will provide the Volume Gradient.
4292 
4293     // Get Current Face Surface Area
4294     PetscScalar bodyVol, bodySA, bodyData[14];
4295     PetscCall(EG_getMassProperties(body, bodyData)); // Does not have an EGlite version KNOWN_ISSUE
4296     bodyVol = bodyData[0];
4297     bodySA  = bodyData[1];
4298 
4299     // Cycle through Control Points
4300     for (int ii = 0; ii < totalNumCPs; ++ii) { // ii should also be the row in cpEquiv for the Control Point
4301       // Cycle through X, Y, Z, W changes
4302       for (int jj = 0; jj < 4; ++jj) {
4303         // Cycle Through Faces
4304         double denomNew = 0.0, denomOld = 0.0;
4305         double deltaCoord = 1.0E-4;
4306         ego    newGeom[Nf];
4307         ego    newFaces[Nf];
4308         for (int kk = 0; kk < Nf; ++kk) {
4309           ego      face;
4310           PetscInt currFID = kk + 1;
4311 
4312           if (islite) {
4313             // Get Current FACE
4314             PetscCallEGADS(EGlite_objectBodyTopo, (body, FACE, currFID, &face));
4315 
4316             // Get Geometry Object for the Current FACE
4317             PetscCallEGADS(EGlite_getTopology, (face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
4318             PetscCallEGADS(EGlite_getGeometry, (fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
4319           } else {
4320             // Get Current FACE
4321             PetscCallEGADS(EG_objectBodyTopo, (body, FACE, currFID, &face));
4322 
4323             // Get Geometry Object for the Current FACE
4324             PetscCallEGADS(EG_getTopology, (face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
4325             PetscCallEGADS(EG_getGeometry, (fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
4326           }
4327 
4328           // Make a new SURFACE Geometry by changing the location of the Control Points
4329           int    prvSize = bpinfo[3] + bpinfo[6] + (4 * bpinfo[2] * bpinfo[5]);
4330           double nbprv[prvSize];
4331 
4332           // Reinitialize nbprv[] values because we only want to change one value at a time
4333           for (int mm = 0; mm < prvSize; ++mm) nbprv[mm] = bprv[mm];
4334 
4335           // Get Control Point Row and Column Start for cpEquiv
4336           PetscHashIter Witer;
4337           PetscBool     Wfound;
4338           PetscInt      faceWStartRow;
4339           PetscCall(PetscHMapIFind(faceCPWeightsRow_Start, currFID, &Witer, &Wfound));
4340           PetscCheck(Wfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "FACE ID not found in Control Point Weights Hash Table");
4341           PetscCall(PetscHMapIGet(faceCPWeightsRow_Start, currFID, &faceWStartRow));
4342 
4343           // Modify the Current Control Point on this FACE and All Other FACES
4344           // IMPORTANT!!! If you do not move all identical Control Points on other FACES
4345           //              you will not generate a solid body. You will generate a set of
4346           //              disconnected surfaces that have gap(s) between them.
4347           int offset  = bpinfo[3] + bpinfo[6];
4348           int wOffset = offset + (3 * bpinfo[2] * bpinfo[5]);
4349           for (int mm = 0; mm < bpinfo[2] * bpinfo[5]; ++mm) {
4350             PetscScalar matValue;
4351             PetscCall(MatGetValue(cpEquiv, ii, faceWStartRow + mm, &matValue));
4352 
4353             if (matValue > 0.0) {
4354               if (jj == 0) { //X
4355                 nbprv[offset + (3 * mm) + 0] = bprv[offset + (3 * mm) + 0] + deltaCoord;
4356                 nbprv[offset + (3 * mm) + 1] = bprv[offset + (3 * mm) + 1];
4357                 nbprv[offset + (3 * mm) + 2] = bprv[offset + (3 * mm) + 2];
4358                 denomNew                     = nbprv[offset + (3 * mm) + 0];
4359                 denomOld                     = bprv[offset + (3 * mm) + 0];
4360               } else if (jj == 1) { //Y
4361                 nbprv[offset + (3 * mm) + 0] = bprv[offset + (3 * mm) + 0];
4362                 nbprv[offset + (3 * mm) + 1] = bprv[offset + (3 * mm) + 1] + deltaCoord;
4363                 nbprv[offset + (3 * mm) + 2] = bprv[offset + (3 * mm) + 2];
4364                 denomNew                     = nbprv[offset + (3 * mm) + 1];
4365                 denomOld                     = bprv[offset + (3 * mm) + 1];
4366               } else if (jj == 2) { //Z
4367                 nbprv[offset + (3 * mm) + 0] = bprv[offset + (3 * mm) + 0];
4368                 nbprv[offset + (3 * mm) + 1] = bprv[offset + (3 * mm) + 1];
4369                 nbprv[offset + (3 * mm) + 2] = bprv[offset + (3 * mm) + 2] + deltaCoord;
4370                 denomNew                     = nbprv[offset + (3 * mm) + 2];
4371                 denomOld                     = bprv[offset + (3 * mm) + 2];
4372               } else if (jj == 3) { // Weights
4373                 nbprv[wOffset + mm] = bprv[wOffset + mm] + deltaCoord;
4374                 denomNew            = nbprv[wOffset + mm];
4375                 denomOld            = bprv[wOffset + mm];
4376               } else {
4377                 // currently do nothing
4378               }
4379             }
4380           }
4381 
4382           // Create New Surface Based on New Control Points or Weights
4383           ego newgeom, context;
4384           PetscCallEGADS(EG_getContext, (face, &context));                                             // Does not have an EGlite_ versions   KNOWN_ISSUE
4385           PetscCallEGADS(EG_makeGeometry, (context, SURFACE, BSPLINE, NULL, bpinfo, nbprv, &newgeom)); // Does not have an EGlite_ version KNOWN_ISSUE
4386 
4387           // Create New FACE based on modified geometry
4388           double newFaceRange[4];
4389           int    newFacePeri;
4390           if (islite) PetscCallEGADS(EGlite_getRange, (newgeom, newFaceRange, &newFacePeri));
4391           else PetscCallEGADS(EG_getRange, (newgeom, newFaceRange, &newFacePeri));
4392 
4393           ego newface;
4394           PetscCallEGADS(EG_makeFace, (newgeom, SFORWARD, newFaceRange, &newface)); // Does not have an EGlite_ version KNOWN_ISSUE
4395 
4396           // store new face for later assembly
4397           newGeom[kk]  = newgeom;
4398           newFaces[kk] = newface;
4399         }
4400 
4401         // X-WANT TO BUILD THE NEW GEOMETRY, X-GET NEW SA AND PERFORM dSA/dCPi CALCS HERE <---
4402         // Sew New Faces together to get a new model
4403         ego newmodel;
4404         PetscCall(EG_sewFaces(Nf, newFaces, 0.0, 0, &newmodel)); // Does not have an EGlite_ version KNOWN_ISSUE
4405 
4406         // Get Surface Area and Volume of New/Updated Solid Body
4407         PetscScalar newData[14];
4408         if (islite) PetscCallEGADS(EGlite_getTopology, (newmodel, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
4409         else PetscCallEGADS(EG_getTopology, (newmodel, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
4410 
4411         ego nbody = bodies[0];
4412         PetscCall(EG_getMassProperties(nbody, newData)); // Does not have an EGlite_ version   KNOWN_ISSUE
4413 
4414         PetscScalar dSAdCPi, dVdCPi;
4415         PetscScalar nbodyVol = newData[0], nbodySA = newData[1];
4416 
4417         // Calculate Gradients wrt to Control Points and Control Points Weights depending on jj value
4418         dSAdCPi = (nbodySA - bodySA) / (denomNew - denomOld);
4419         dVdCPi  = (nbodyVol - bodyVol) / (denomNew - denomOld);
4420 
4421         if (jj < 3) {
4422           // Gradienst wrt to Control Points
4423           gradSACP[(ii * 3) + jj] = dSAdCPi;
4424           gradVCP[(ii * 3) + jj]  = dVdCPi;
4425         } else if (jj == 3) {
4426           // Gradients wrt to Control Point Weights
4427           gradSAW[ii] = dSAdCPi;
4428           gradVW[ii]  = dVdCPi;
4429         } else {
4430           // Do Nothing
4431         }
4432         PetscCallEGADS(EG_deleteObject, (newmodel));
4433         for (int kk = 0; kk < Nf; ++kk) {
4434           PetscCallEGADS(EG_deleteObject, (newFaces[kk]));
4435           PetscCallEGADS(EG_deleteObject, (newGeom[kk]));
4436         }
4437       }
4438     }
4439   }
4440   PetscCall(VecRestoreArrayWrite(gradSACPVec, &gradSACP));
4441   PetscCall(VecRestoreArrayWrite(gradSAWVec, &gradSAW));
4442   PetscCall(VecRestoreArrayWrite(gradVCPVec, &gradVCP));
4443   PetscCall(VecRestoreArrayWrite(gradVWVec, &gradVW));
4444   PetscCall(MatDestroy(&cpEquiv));
4445 
4446   // Attach Surface Gradient Hash Table and Matrix to DM
4447   {
4448     PetscContainer surfGradOrgObj;
4449 
4450     PetscCall(PetscObjectQuery((PetscObject)dm, "Surface Gradient Hash Table", (PetscObject *)&surfGradOrgObj));
4451     if (!surfGradOrgObj) {
4452       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &surfGradOrgObj));
4453       PetscCall(PetscContainerSetPointer(surfGradOrgObj, pointSurfGradRow_Start));
4454       PetscCall(PetscContainerSetCtxDestroy(surfGradOrgObj, DestroyHashMap));
4455       PetscCall(PetscObjectCompose((PetscObject)dm, "Surface Gradient Hash Table", (PetscObject)surfGradOrgObj));
4456       PetscCall(PetscContainerDestroy(&surfGradOrgObj));
4457     } else {
4458       PetscCall(PetscContainerSetPointer(surfGradOrgObj, pointSurfGradRow_Start));
4459     }
4460 
4461     PetscCall(PetscObjectCompose((PetscObject)dm, "Surface Gradient Matrix", (PetscObject)pointSurfGrad));
4462     PetscCall(MatDestroy(&pointSurfGrad));
4463 
4464     PetscCall(PetscObjectCompose((PetscObject)dm, "Surface Area Control Point Gradient", (PetscObject)gradSACPVec));
4465     PetscCall(VecDestroy(&gradSACPVec));
4466 
4467     PetscCall(PetscObjectCompose((PetscObject)dm, "Surface Area Weights Gradient", (PetscObject)gradSAWVec));
4468     PetscCall(VecDestroy(&gradSAWVec));
4469 
4470     if (fullGeomGrad) {
4471       PetscCall(PetscObjectCompose((PetscObject)dm, "Volume Control Point Gradient", (PetscObject)gradVCPVec));
4472       PetscCall(PetscObjectCompose((PetscObject)dm, "Volume Weights Gradient", (PetscObject)gradVWVec));
4473     }
4474     PetscCall(VecDestroy(&gradVCPVec));
4475     PetscCall(VecDestroy(&gradVWVec));
4476   }
4477 
4478   // Could be replaced with DMPlexFreeGeomObject()
4479   if (islite) EGlite_free(fobjs);
4480   else EG_free(fobjs);
4481   PetscFunctionReturn(PETSC_SUCCESS);
4482 #else
4483   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "This method requires EGADS support. Reconfigure using --download-egads");
4484 #endif
4485 }
4486 
4487 /*@C
4488   DMPlexModifyGeomModel - Generates a new EGADS geometry model based in user provided Control Points and Control Points Weights. Optionally, the function will inflate the DM to the new geometry and save the new geometry to a file.
4489 
4490   Collective
4491 
4492   Input Parameters:
4493 + dm          - The DM object representing the mesh with PetscContainer containing an EGADS geometry model
4494 . comm        - MPI_Comm object
4495 . newCP       - C Array of [x, y, z] New/Updated Control Point Coordinates defining the geometry (See DMPlexGeomDataAndGrads() for format)
4496 . newW        - C Array of New/Updated Control Point Weights associated with the Control Points defining the new geometry (See DMPlexGemGrads() for format)
4497 . autoInflate - PetscBool Flag denoting if the user would like to inflate the DM points to the new geometry.
4498 . saveGeom    - PetscBool Flag denoting if the user would iike to save the new geometry to a file.
4499 - stpName     - Char Array indicating the name of the file to save the new geometry to. Extension must be included and will denote type of file written.
4500                       *.stp or *.step = STEP File
4501                       *.igs or *.iges = IGES File
4502                               *.egads = EGADS File
4503                                *.brep = BRep File (OpenCASCADE File)
4504 
4505   Output Parameter:
4506 . dm - The updated DM object representing the mesh with PetscContainers containing the updated/modified geometry
4507 
4508   Level: intermediate
4509 
4510   Note:
4511   Functionality not available for DMPlexes with attached EGADSlite geometry files (.egadslite).
4512 
4513 .seealso: `DMPLEX`, `DMCreate()`, `DMPlexCreateGeom()`, `DMPlexGeomDataAndGrads()`
4514 @*/
4515 PetscErrorCode DMPlexModifyGeomModel(DM dm, MPI_Comm comm, PetscScalar newCP[], PetscScalar newW[], PetscBool autoInflate, PetscBool saveGeom, const char *stpName) PeNS
4516 {
4517 #if defined(PETSC_HAVE_EGADS)
4518   /* EGADS/EGADSlite variables */
4519   ego context, model, geom, *bodies, *lobjs, *fobjs;
4520   int oclass, mtype, *senses, *lsenses;
4521   int Nb, Nf, Nl, id;
4522   /* PETSc variables */
4523   DMLabel        bodyLabel, faceLabel, edgeLabel, vertexLabel;
4524   PetscContainer modelObj, cpHashTableObj, wHashTableObj;
4525   PetscHMapI     cpHashTable = NULL, wHashTable = NULL;
4526   PetscBool      islite = PETSC_FALSE;
4527 #endif
4528 
4529 #if defined(PETSC_HAVE_EGADS)
4530   PetscFunctionBegin;
4531   // Look to see if DM has a Container with either a EGADS or EGADSlite Model
4532   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
4533   if (!modelObj) {
4534     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
4535     islite = PETSC_TRUE;
4536   }
4537   PetscCheck(!islite, PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot modify geometries defined by EGADSlite (.egadslite)! Please use another geometry file format STEP, IGES, EGADS or BRep");
4538   PetscCheck(modelObj, PETSC_COMM_SELF, PETSC_ERR_SUP, "DM does not have a EGADS Geometry Model attached to it!");
4539 
4540   // Get attached EGADS model (pointer)
4541   PetscCall(PetscContainerGetPointer(modelObj, (void **)&model));
4542 
4543   // Look to see if DM has Container for Geometry Control Point Data
4544   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Hash Table", (PetscObject *)&cpHashTableObj));
4545   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weights Hash Table", (PetscObject *)&wHashTableObj));
4546 
4547   PetscCheck(cpHashTableObj && wHashTableObj, PETSC_COMM_SELF, PETSC_ERR_SUP, "DM does not have required Geometry Data attached! Please run DMPlexGeomDataAndGrads() Function first.");
4548 
4549   // Get attached EGADS model Control Point and Weights Hash Tables and Data Arrays (pointer)
4550   PetscCall(PetscContainerGetPointer(cpHashTableObj, (void **)&cpHashTable));
4551   PetscCall(PetscContainerGetPointer(wHashTableObj, (void **)&wHashTable));
4552 
4553   // Get the number of bodies and body objects in the model
4554   if (islite) PetscCallEGADS(EGlite_getTopology, (model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
4555   else PetscCallEGADS(EG_getTopology, (model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
4556 
4557   // Get all Faces on the body
4558   ego body = bodies[0];
4559   if (islite) PetscCallEGADS(EGlite_getBodyTopos, (body, NULL, FACE, &Nf, &fobjs));
4560   else PetscCallEGADS(EG_getBodyTopos, (body, NULL, FACE, &Nf, &fobjs));
4561 
4562   ego newGeom[Nf];
4563   ego newFaces[Nf];
4564 
4565   // Update Control Point and Weight definitions for each surface
4566   for (int jj = 0; jj < Nf; ++jj) {
4567     ego     face = fobjs[jj];
4568     ego     bRef, bPrev, bNext;
4569     ego     fgeom;
4570     int     offset;
4571     int     boclass, bmtype, *bpinfo;
4572     double *bprv;
4573 
4574     // Get FACE ID and other Geometry Data
4575     if (islite) {
4576       id = EGlite_indexBodyTopo(body, face);
4577       PetscCallEGADS(EGlite_getTopology, (face, &fgeom, &oclass, &mtype, NULL, &Nl, &lobjs, &lsenses));
4578       PetscCallEGADS(EGlite_getGeometry, (fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
4579       PetscCallEGADS(EGlite_getInfo, (fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
4580     } else {
4581       id = EG_indexBodyTopo(body, face);
4582       PetscCallEGADS(EG_getTopology, (face, &fgeom, &oclass, &mtype, NULL, &Nl, &lobjs, &lsenses));
4583       PetscCallEGADS(EG_getGeometry, (fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
4584       PetscCallEGADS(EG_getInfo, (fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
4585     }
4586 
4587     // Update Control Points
4588     PetscHashIter CPiter, Witer;
4589     PetscBool     CPfound, Wfound;
4590     PetscInt      faceCPStartRow, faceWStartRow;
4591 
4592     PetscCall(PetscHMapIFind(cpHashTable, id, &CPiter, &CPfound));
4593     PetscCheck(CPfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "FACE ID not found in Control Point Hash Table");
4594     PetscCall(PetscHMapIGet(cpHashTable, id, &faceCPStartRow));
4595 
4596     PetscCall(PetscHMapIFind(wHashTable, id, &Witer, &Wfound));
4597     PetscCheck(Wfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "FACE ID not found in Control Point Weights Hash Table");
4598     PetscCall(PetscHMapIGet(wHashTable, id, &faceWStartRow));
4599 
4600     // UPDATE CONTROL POINTS Locations
4601     offset = bpinfo[3] + bpinfo[6];
4602     for (int ii = 0; ii < 3 * bpinfo[2] * bpinfo[5]; ++ii) { bprv[offset + ii] = newCP[faceCPStartRow + ii]; }
4603 
4604     // UPDATE CONTROL POINT WEIGHTS
4605     offset = bpinfo[3] + bpinfo[6] + 3 * bpinfo[2] * bpinfo[5];
4606     for (int ii = 0; ii < bpinfo[2] * bpinfo[5]; ++ii) { bprv[offset + ii] = newW[faceWStartRow + ii]; }
4607 
4608     // Get Context from FACE
4609     context = NULL;
4610     PetscCallEGADS(EG_getContext, (face, &context)); // Does not have an EGlite_ version  KNOWN_ISSUE
4611 
4612     // Create New Surface
4613     ego newgeom;
4614     PetscCallEGADS(EG_makeGeometry, (context, SURFACE, BSPLINE, NULL, bpinfo, bprv, &newgeom)); // Does not have an EGlite_ version KNOWN_ISSUE
4615 
4616     // Create new FACE based on new SURFACE geometry
4617     double data[4];
4618     int    periodic;
4619     if (islite) PetscCallEGADS(EGlite_getRange, (newgeom, data, &periodic));
4620     else PetscCallEGADS(EG_getRange, (newgeom, data, &periodic));
4621 
4622     ego newface;
4623     PetscCallEGADS(EG_makeFace, (newgeom, SFORWARD, data, &newface)); // Does not have an EGlite_ version KNOWN_ISSUE
4624     newGeom[jj]  = newgeom;
4625     newFaces[jj] = newface;
4626   }
4627   // Could be replaced by DMPlexFreeGeomObject
4628   if (islite) EGlite_free(fobjs);
4629   else EG_free(fobjs);
4630 
4631   // Sew New Faces together to get a new model
4632   ego newmodel;
4633   PetscCall(EG_sewFaces(Nf, newFaces, 0.0, 0, &newmodel)); // Does not have an EGlite_ version   KNOWN_ISSUE
4634   for (PetscInt f = 0; f < Nf; ++f) {
4635     PetscCallEGADS(EG_deleteObject, (newFaces[f]));
4636     PetscCallEGADS(EG_deleteObject, (newGeom[f]));
4637   }
4638 
4639   // Get the total number of NODEs on the original geometry. (This will be the same for the new geometry)
4640   int  totalNumNode;
4641   ego *nobjTotal;
4642   if (islite) {
4643     PetscCallEGADS(EGlite_getBodyTopos, (body, NULL, NODE, &totalNumNode, &nobjTotal));
4644     EGlite_free(nobjTotal);
4645   } else {
4646     PetscCallEGADS(EG_getBodyTopos, (body, NULL, NODE, &totalNumNode, &nobjTotal));
4647     EG_free(nobjTotal);
4648   } // Could be replaced with DMPlexFreeGeomObject
4649 
4650   // Initialize vector to store equivalent NODE indices between the 2 geometries
4651   // FORMAT :: vector index is the Original Geometry's NODE ID, the vector Value is the New Geometry's NODE ID
4652   int nodeIDEquiv[totalNumNode + 1];
4653 
4654   // Now we need to Map the NODE and EDGE IDs from each Model
4655   if (islite) PetscCallEGADS(EGlite_getBodyTopos, (body, NULL, FACE, &Nf, &fobjs));
4656   else PetscCallEGADS(EG_getBodyTopos, (body, NULL, FACE, &Nf, &fobjs));
4657 
4658   // New CAD
4659   ego *newbodies, newgeomtest, *nfobjs;
4660   int  nNf, newNb, newoclass, newmtype, *newsenses;
4661   if (islite) PetscCallEGADS(EGlite_getTopology, (newmodel, &newgeomtest, &newoclass, &newmtype, NULL, &newNb, &newbodies, &newsenses));
4662   else PetscCallEGADS(EG_getTopology, (newmodel, &newgeomtest, &newoclass, &newmtype, NULL, &newNb, &newbodies, &newsenses));
4663 
4664   ego newbody = newbodies[0];
4665   if (islite) PetscCallEGADS(EGlite_getBodyTopos, (newbody, NULL, FACE, &nNf, &nfobjs));
4666   else PetscCallEGADS(EG_getBodyTopos, (newbody, NULL, FACE, &nNf, &nfobjs));
4667 
4668   PetscCheck(newNb == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "ERROR :: newNb > 1 || newNb = %d", newNb);
4669 
4670   // Find Equivalent Nodes
4671   for (int ii = 0; ii < Nf; ++ii) {
4672     double fdata[4];
4673     int    peri;
4674 
4675     // Get Current FACE [u, v] Ranges
4676     if (islite) PetscCallEGADS(EGlite_getRange, (fobjs[ii], fdata, &peri));
4677     else PetscCallEGADS(EG_getRange, (fobjs[ii], fdata, &peri));
4678 
4679     // Equate NODE IDs between 2 FACEs by working through (u, v) limits of FACE
4680     for (int jj = 0; jj < 2; ++jj) {
4681       for (int kk = 2; kk < 4; ++kk) {
4682         double params[2] = {fdata[jj], fdata[kk]};
4683         double eval[18];
4684         if (islite) PetscCallEGADS(EGlite_evaluate, (fobjs[ii], params, eval));
4685         else PetscCallEGADS(EG_evaluate, (fobjs[ii], params, eval));
4686 
4687         // Original Body
4688         ego *nobjsOrigFace;
4689         int  origNn;
4690         if (islite) PetscCallEGADS(EGlite_getBodyTopos, (body, fobjs[ii], NODE, &origNn, &nobjsOrigFace));
4691         else PetscCallEGADS(EG_getBodyTopos, (body, fobjs[ii], NODE, &origNn, &nobjsOrigFace));
4692 
4693         double minVal = 1.0E10;
4694         double evalCheck[18];
4695         int    equivOrigNodeID = -1;
4696         for (int mm = 0; mm < origNn; ++mm) {
4697           double delta = 1.0E10;
4698           if (islite) PetscCallEGADS(EGlite_evaluate, (nobjsOrigFace[mm], NULL, evalCheck));
4699           else PetscCallEGADS(EG_evaluate, (nobjsOrigFace[mm], NULL, evalCheck));
4700 
4701           delta = PetscSqrtReal(PetscSqr(evalCheck[0] - eval[0]) + PetscSqr(evalCheck[1] - eval[1]) + PetscSqr(evalCheck[2] - eval[2]));
4702 
4703           if (delta < minVal) {
4704             if (islite) equivOrigNodeID = EGlite_indexBodyTopo(body, nobjsOrigFace[mm]);
4705             else equivOrigNodeID = EG_indexBodyTopo(body, nobjsOrigFace[mm]);
4706 
4707             minVal = delta;
4708           }
4709         }
4710         // Could be replaced with DMPlexFreeGeomObject
4711         if (islite) EGlite_free(nobjsOrigFace);
4712         else EG_free(nobjsOrigFace);
4713 
4714         // New Body
4715         ego *nobjsNewFace;
4716         int  newNn;
4717         if (islite) PetscCallEGADS(EGlite_getBodyTopos, (newbody, nfobjs[ii], NODE, &newNn, &nobjsNewFace));
4718         else PetscCallEGADS(EG_getBodyTopos, (newbody, nfobjs[ii], NODE, &newNn, &nobjsNewFace));
4719 
4720         minVal             = 1.0E10;
4721         int equivNewNodeID = -1;
4722         for (int mm = 0; mm < newNn; ++mm) {
4723           double delta = 1.0E10;
4724           if (islite) PetscCallEGADS(EGlite_evaluate, (nobjsNewFace[mm], NULL, evalCheck));
4725           else PetscCallEGADS(EG_evaluate, (nobjsNewFace[mm], NULL, evalCheck));
4726 
4727           delta = PetscSqrtReal(PetscSqr(evalCheck[0] - eval[0]) + PetscSqr(evalCheck[1] - eval[1]) + PetscSqr(evalCheck[2] - eval[2]));
4728 
4729           if (delta < minVal) {
4730             if (islite) equivNewNodeID = EGlite_indexBodyTopo(newbody, nobjsNewFace[mm]);
4731             else equivNewNodeID = EG_indexBodyTopo(newbody, nobjsNewFace[mm]);
4732 
4733             minVal = delta;
4734           }
4735         }
4736         if (islite) EGlite_free(nobjsNewFace);
4737         else EG_free(nobjsNewFace);
4738 
4739         // Store equivalent NODE IDs
4740         nodeIDEquiv[equivOrigNodeID] = equivNewNodeID;
4741       }
4742     }
4743   }
4744 
4745   // Find Equivalent EDGEs
4746   //   Get total number of EDGEs on Original Geometry
4747   int  totalNumEdge;
4748   ego *eobjsOrig;
4749   if (islite) {
4750     PetscCallEGADS(EGlite_getBodyTopos, (body, NULL, EDGE, &totalNumEdge, &eobjsOrig));
4751     EGlite_free(eobjsOrig);
4752   } else {
4753     PetscCallEGADS(EG_getBodyTopos, (body, NULL, EDGE, &totalNumEdge, &eobjsOrig));
4754     EG_free(eobjsOrig);
4755   }
4756 
4757   //   Get total number of EDGEs on New Geometry
4758   int  totalNumEdgeNew;
4759   ego *eobjsNew;
4760   if (islite) {
4761     PetscCallEGADS(EGlite_getBodyTopos, (newbody, NULL, EDGE, &totalNumEdgeNew, &eobjsNew));
4762     EGlite_free(eobjsNew);
4763   } else {
4764     PetscCallEGADS(EG_getBodyTopos, (newbody, NULL, EDGE, &totalNumEdgeNew, &eobjsNew));
4765     EG_free(eobjsNew);
4766   }
4767 
4768   // Initialize EDGE ID equivalent vector
4769   // FORMAT :: vector index is the Original Geometry's EDGE ID, the vector Value is the New Geometry's EDGE ID
4770   int edgeIDEquiv[totalNumEdge + 1];
4771 
4772   // Find Equivalent EDGEs
4773   for (int ii = 0; ii < Nf; ++ii) {
4774     // Get Original Geometry EDGE's NODEs
4775     int numOrigEdge, numNewEdge;
4776     if (islite) {
4777       PetscCallEGADS(EGlite_getBodyTopos, (body, fobjs[ii], EDGE, &numOrigEdge, &eobjsOrig));
4778       PetscCallEGADS(EGlite_getBodyTopos, (newbody, nfobjs[ii], EDGE, &numNewEdge, &eobjsNew));
4779     } else {
4780       PetscCallEGADS(EG_getBodyTopos, (body, fobjs[ii], EDGE, &numOrigEdge, &eobjsOrig));
4781       PetscCallEGADS(EG_getBodyTopos, (newbody, nfobjs[ii], EDGE, &numNewEdge, &eobjsNew));
4782     }
4783 
4784     // new loop below
4785     for (int nn = 0; nn < numOrigEdge; ++nn) {
4786       ego origEdge = eobjsOrig[nn];
4787       ego geomEdgeOrig, *nobjsOrig;
4788       int oclassEdgeOrig, mtypeEdgeOrig;
4789       int NnOrig, *nsensesEdgeOrig;
4790 
4791       if (islite) PetscCallEGADS(EGlite_getTopology, (origEdge, &geomEdgeOrig, &oclassEdgeOrig, &mtypeEdgeOrig, NULL, &NnOrig, &nobjsOrig, &nsensesEdgeOrig));
4792       else PetscCallEGADS(EG_getTopology, (origEdge, &geomEdgeOrig, &oclassEdgeOrig, &mtypeEdgeOrig, NULL, &NnOrig, &nobjsOrig, &nsensesEdgeOrig));
4793 
4794       PetscBool isSame = PETSC_FALSE;
4795       for (int jj = 0; jj < numNewEdge; ++jj) {
4796         ego newEdge = eobjsNew[jj];
4797         ego geomEdgeNew, *nobjsNew;
4798         int oclassEdgeNew, mtypeEdgeNew;
4799         int NnNew, *nsensesEdgeNew;
4800 
4801         if (islite) PetscCallEGADS(EGlite_getTopology, (newEdge, &geomEdgeNew, &oclassEdgeNew, &mtypeEdgeNew, NULL, &NnNew, &nobjsNew, &nsensesEdgeNew));
4802         else PetscCallEGADS(EG_getTopology, (newEdge, &geomEdgeNew, &oclassEdgeNew, &mtypeEdgeNew, NULL, &NnNew, &nobjsNew, &nsensesEdgeNew));
4803 
4804         if (mtypeEdgeOrig == mtypeEdgeNew) {
4805           // Only operate if the EDGE types are the same
4806           for (int kk = 0; kk < NnNew; ++kk) {
4807             int nodeIDOrigGeom, nodeIDNewGeom;
4808             if (islite) {
4809               nodeIDOrigGeom = EGlite_indexBodyTopo(body, nobjsOrig[kk]);
4810               nodeIDNewGeom  = EGlite_indexBodyTopo(newbody, nobjsNew[kk]);
4811             } else {
4812               nodeIDOrigGeom = EG_indexBodyTopo(body, nobjsOrig[kk]);
4813               nodeIDNewGeom  = EG_indexBodyTopo(newbody, nobjsNew[kk]);
4814             }
4815 
4816             if (nodeIDNewGeom == nodeIDEquiv[nodeIDOrigGeom]) {
4817               isSame = PETSC_TRUE;
4818             } else {
4819               isSame = PETSC_FALSE;
4820               kk     = NnNew; // skip ahead because first NODE failed test and order is important
4821             }
4822           }
4823 
4824           if (isSame == PETSC_TRUE) {
4825             int edgeIDOrig, edgeIDNew;
4826             if (islite) {
4827               edgeIDOrig = EGlite_indexBodyTopo(body, origEdge);
4828               edgeIDNew  = EGlite_indexBodyTopo(newbody, newEdge);
4829             } else {
4830               edgeIDOrig = EG_indexBodyTopo(body, origEdge);
4831               edgeIDNew  = EG_indexBodyTopo(newbody, newEdge);
4832             }
4833 
4834             edgeIDEquiv[edgeIDOrig] = edgeIDNew;
4835             jj                      = numNewEdge;
4836           }
4837         }
4838       }
4839     }
4840     if (islite) {
4841       EGlite_free(eobjsOrig);
4842       EGlite_free(eobjsNew);
4843     } else {
4844       EG_free(eobjsOrig);
4845       EG_free(eobjsNew);
4846     }
4847   }
4848   if (islite) {
4849     EGlite_free(fobjs);
4850     EGlite_free(nfobjs);
4851   } else {
4852     EG_free(fobjs);
4853     EG_free(nfobjs);
4854   }
4855 
4856   // Modify labels to point to the IDs on the new Geometry
4857   IS isNodeID, isEdgeID;
4858 
4859   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
4860   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
4861   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
4862   PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));
4863 
4864   PetscCall(ISCreateGeneral(comm, totalNumNode + 1, nodeIDEquiv, PETSC_COPY_VALUES, &isNodeID));
4865   PetscCall(ISCreateGeneral(comm, totalNumEdge + 1, edgeIDEquiv, PETSC_COPY_VALUES, &isEdgeID));
4866   /* Do not perform check. Np may != Nv due to Degenerate Geometry which is not stored in labels.               */
4867   /* We do not know in advance which IDs have been omitted. This may also change due to geometry modifications. */
4868   PetscCall(DMLabelRewriteValues(vertexLabel, isNodeID));
4869   PetscCall(DMLabelRewriteValues(edgeLabel, isEdgeID));
4870   PetscCall(ISDestroy(&isNodeID));
4871   PetscCall(ISDestroy(&isEdgeID));
4872 
4873   // Attempt to point to the new geometry
4874   PetscCallEGADS(EG_deleteObject, (model));
4875   PetscCall(PetscContainerSetPointer(modelObj, newmodel));
4876 
4877   // save updated model to file
4878   if (saveGeom == PETSC_TRUE && stpName != NULL) PetscCall(EG_saveModel(newmodel, stpName));
4879 
4880   // Inflate Mesh to EGADS Model
4881   if (autoInflate == PETSC_TRUE) PetscCall(DMPlexInflateToGeomModel(dm, PETSC_TRUE));
4882   PetscFunctionReturn(PETSC_SUCCESS);
4883 #else
4884   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "This method requires EGADS support. Reconfigure using --download-egads");
4885 #endif
4886 }
4887 
4888 /*@C
4889   DMPlexGetGeomModelTUV - Gets the [t] (EDGES) and [u, v] (FACES) geometry parameters of DM points that are associated geometry relationships. Requires a DM with a EGADS model attached.
4890 
4891   Collective
4892 
4893   Input Parameter:
4894 . dm - The DM object representing the mesh with PetscContainer containing an EGADS geometry model
4895 
4896   Level: intermediate
4897 
4898 .seealso: `DMPLEX`, `DMCreate()`, `DMPlexCreateGeom()`, `DMPlexGeomDataAndGrads()`
4899 @*/
4900 PetscErrorCode DMPlexGetGeomModelTUV(DM dm) PeNS
4901 {
4902 #if defined(PETSC_HAVE_EGADS)
4903   /* EGADS Variables */
4904   ego    model, geom, body, face, edge;
4905   ego   *bodies;
4906   int    Nb, oclass, mtype, *senses;
4907   double result[4];
4908   /* PETSc Variables */
4909   DM             cdm;
4910   PetscContainer modelObj;
4911   DMLabel        bodyLabel, faceLabel, edgeLabel, vertexLabel;
4912   Vec            coordinates;
4913   PetscScalar   *coords;
4914   PetscInt       bodyID, faceID, edgeID, vertexID;
4915   PetscInt       cdim, vStart, vEnd, v;
4916   PetscBool      islite = PETSC_FALSE;
4917 #endif
4918 
4919   PetscFunctionBegin;
4920 #if defined(PETSC_HAVE_EGADS)
4921   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
4922   if (!modelObj) {
4923     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
4924     islite = PETSC_TRUE;
4925   }
4926   if (!modelObj) PetscFunctionReturn(0);
4927 
4928   PetscCall(DMGetCoordinateDim(dm, &cdim));
4929   PetscCall(DMGetCoordinateDM(dm, &cdm));
4930   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
4931   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
4932   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
4933   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
4934   PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));
4935 
4936   PetscCall(PetscContainerGetPointer(modelObj, (void **)&model));
4937 
4938   if (islite) PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
4939   else PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
4940 
4941   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
4942   PetscCall(VecGetArrayWrite(coordinates, &coords));
4943 
4944   // Define t, u, v arrays to be stored in a PetscContainer after populated
4945   PetscScalar *t_point, *u_point, *v_point;
4946   PetscCall(PetscMalloc1(vEnd - vStart, &t_point));
4947   PetscCall(PetscMalloc1(vEnd - vStart, &u_point));
4948   PetscCall(PetscMalloc1(vEnd - vStart, &v_point));
4949 
4950   for (v = vStart; v < vEnd; ++v) {
4951     PetscScalar *vcoords;
4952 
4953     PetscCall(DMLabelGetValue(bodyLabel, v, &bodyID));
4954     PetscCall(DMLabelGetValue(faceLabel, v, &faceID));
4955     PetscCall(DMLabelGetValue(edgeLabel, v, &edgeID));
4956     PetscCall(DMLabelGetValue(vertexLabel, v, &vertexID));
4957 
4958     // TODO Figure out why this is unknown sometimes
4959     if (bodyID < 0 && Nb == 1) bodyID = 0;
4960     PetscCheck(bodyID >= 0 && bodyID < Nb, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Body %" PetscInt_FMT " for vertex %" PetscInt_FMT " is not in [0, %d)", bodyID, v, Nb);
4961     body = bodies[bodyID];
4962 
4963     PetscCall(DMPlexPointLocalRef(cdm, v, coords, (void *)&vcoords));
4964     if (edgeID > 0) {
4965       /* Snap to EDGE at nearest location */
4966       double params[1];
4967 
4968       if (islite) {
4969         PetscCall(EGlite_objectBodyTopo(body, EDGE, edgeID, &edge));
4970         PetscCall(EGlite_invEvaluate(edge, vcoords, params, result));
4971       } // Get (t) of nearest point on EDGE
4972       else {
4973         PetscCall(EG_objectBodyTopo(body, EDGE, edgeID, &edge));
4974         PetscCall(EG_invEvaluate(edge, vcoords, params, result));
4975       } // Get (t) of nearest point on EDGE
4976 
4977       t_point[v - vStart] = params[0];
4978       u_point[v - vStart] = 0.0;
4979       v_point[v - vStart] = 0.0;
4980     } else if (faceID > 0) {
4981       /* Snap to FACE at nearest location */
4982       double params[2];
4983 
4984       if (islite) {
4985         PetscCall(EGlite_objectBodyTopo(body, FACE, faceID, &face));
4986         PetscCall(EGlite_invEvaluate(face, vcoords, params, result));
4987       } // Get (x,y,z) of nearest point on FACE
4988       else {
4989         PetscCall(EG_objectBodyTopo(body, FACE, faceID, &face));
4990         PetscCall(EG_invEvaluate(face, vcoords, params, result));
4991       } // Get (x,y,z) of nearest point on FACE
4992 
4993       t_point[v - vStart] = 0.0;
4994       u_point[v - vStart] = params[0];
4995       v_point[v - vStart] = params[1];
4996     } else {
4997       t_point[v - vStart] = 0.0;
4998       u_point[v - vStart] = 0.0;
4999       v_point[v - vStart] = 0.0;
5000     }
5001   }
5002   PetscCall(VecRestoreArrayWrite(coordinates, &coords));
5003   /* Clear out global coordinates */
5004   PetscCall(VecDestroy(&dm->coordinates[0].x));
5005 
5006   /* Store in PetscContainters */
5007   {
5008     PetscContainer t_pointObj, u_pointObj, v_pointObj;
5009 
5010     PetscCall(PetscObjectQuery((PetscObject)dm, "Point - Edge t Parameter", (PetscObject *)&t_pointObj));
5011     if (!t_pointObj) {
5012       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &t_pointObj));
5013       PetscCall(PetscContainerSetPointer(t_pointObj, t_point));
5014       PetscCall(PetscObjectCompose((PetscObject)dm, "Point - Edge t Parameter", (PetscObject)t_pointObj));
5015       PetscCall(PetscContainerSetCtxDestroy(t_pointObj, PetscCtxDestroyDefault));
5016       PetscCall(PetscContainerDestroy(&t_pointObj));
5017     } else {
5018       void *old;
5019 
5020       PetscCall(PetscContainerGetPointer(t_pointObj, &old));
5021       PetscCall(PetscFree(old));
5022       PetscCall(PetscContainerSetPointer(t_pointObj, t_point));
5023     }
5024 
5025     PetscCall(PetscObjectQuery((PetscObject)dm, "Point - Face u Parameter", (PetscObject *)&u_pointObj));
5026     if (!u_pointObj) {
5027       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &u_pointObj));
5028       PetscCall(PetscContainerSetPointer(u_pointObj, u_point));
5029       PetscCall(PetscObjectCompose((PetscObject)dm, "Point - Face u Parameter", (PetscObject)u_pointObj));
5030       PetscCall(PetscContainerSetCtxDestroy(u_pointObj, PetscCtxDestroyDefault));
5031       PetscCall(PetscContainerDestroy(&u_pointObj));
5032     } else {
5033       void *old;
5034 
5035       PetscCall(PetscContainerGetPointer(u_pointObj, &old));
5036       PetscCall(PetscFree(old));
5037       PetscCall(PetscContainerSetPointer(u_pointObj, u_point));
5038     }
5039 
5040     PetscCall(PetscObjectQuery((PetscObject)dm, "Point - Face v Parameter", (PetscObject *)&v_pointObj));
5041     if (!v_pointObj) {
5042       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &v_pointObj));
5043       PetscCall(PetscContainerSetPointer(v_pointObj, v_point));
5044       PetscCall(PetscObjectCompose((PetscObject)dm, "Point - Face v Parameter", (PetscObject)v_pointObj));
5045       PetscCall(PetscContainerSetCtxDestroy(v_pointObj, PetscCtxDestroyDefault));
5046       PetscCall(PetscContainerDestroy(&v_pointObj));
5047     } else {
5048       void *old;
5049 
5050       PetscCall(PetscContainerGetPointer(v_pointObj, &old));
5051       PetscCall(PetscFree(old));
5052       PetscCall(PetscContainerSetPointer(v_pointObj, v_point));
5053     }
5054   }
5055 #endif
5056   PetscFunctionReturn(PETSC_SUCCESS);
5057 }
5058 
5059 /*@C
5060   DMPlexInflateToGeomModelUseTUV - Inflates the DM to the associated underlying geometry using the [t] {EDGES) and [u, v] (FACES} associated parameters. Requires a DM with an EGADS model attached and a previous call to DMPlexGetGeomModelTUV().
5061 
5062   Collective
5063 
5064   Input Parameter:
5065 . dm - The DM object representing the mesh with PetscContainer containing an EGADS geometry model
5066 
5067   Level: intermediate
5068 
5069   Note:
5070   The updated DM object inflated to the associated underlying geometry. This updates the [x, y, z] coordinates of DM points associated with geometry.
5071 
5072 .seealso: `DMPLEX`, `DMCreate()`, `DMPlexCreateGeom()`, `DMPlexGeomDataAndGrads()`, `DMPlexGetGeomModelTUV()`
5073 @*/
5074 PetscErrorCode DMPlexInflateToGeomModelUseTUV(DM dm) PeNS
5075 {
5076 #if defined(PETSC_HAVE_EGADS)
5077   /* EGADS Variables */
5078   ego    model, geom, body, face, edge, vertex;
5079   ego   *bodies;
5080   int    Nb, oclass, mtype, *senses;
5081   double result[18], params[2];
5082   /* PETSc Variables */
5083   DM             cdm;
5084   PetscContainer modelObj;
5085   PetscContainer t_pointObj, u_pointObj, v_pointObj;
5086   DMLabel        bodyLabel, faceLabel, edgeLabel, vertexLabel;
5087   Vec            coordinates;
5088   PetscScalar   *coords;
5089   PetscScalar   *t_point, *u_point, *v_point;
5090   PetscInt       bodyID, faceID, edgeID, vertexID;
5091   PetscInt       cdim, d, vStart, vEnd, v;
5092   PetscBool      islite = PETSC_FALSE;
5093 #endif
5094 
5095   PetscFunctionBegin;
5096 #if defined(PETSC_HAVE_EGADS)
5097   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5098   if (!modelObj) {
5099     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5100     islite = PETSC_TRUE;
5101   }
5102 
5103   PetscCall(PetscObjectQuery((PetscObject)dm, "Point - Edge t Parameter", (PetscObject *)&t_pointObj));
5104   PetscCall(PetscObjectQuery((PetscObject)dm, "Point - Face u Parameter", (PetscObject *)&u_pointObj));
5105   PetscCall(PetscObjectQuery((PetscObject)dm, "Point - Face v Parameter", (PetscObject *)&v_pointObj));
5106 
5107   if (!modelObj) PetscFunctionReturn(PETSC_SUCCESS);
5108   if (!t_pointObj) PetscFunctionReturn(PETSC_SUCCESS);
5109   if (!u_pointObj) PetscFunctionReturn(PETSC_SUCCESS);
5110   if (!v_pointObj) PetscFunctionReturn(PETSC_SUCCESS);
5111 
5112   PetscCall(DMGetCoordinateDim(dm, &cdim));
5113   PetscCall(DMGetCoordinateDM(dm, &cdm));
5114   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
5115   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
5116   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
5117   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
5118   PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));
5119 
5120   PetscCall(PetscContainerGetPointer(t_pointObj, (void **)&t_point));
5121   PetscCall(PetscContainerGetPointer(u_pointObj, (void **)&u_point));
5122   PetscCall(PetscContainerGetPointer(v_pointObj, (void **)&v_point));
5123 
5124   PetscCall(PetscContainerGetPointer(modelObj, (void **)&model));
5125 
5126   if (islite) {
5127     PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
5128   } else {
5129     PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
5130   }
5131 
5132   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
5133   PetscCall(VecGetArrayWrite(coordinates, &coords));
5134 
5135   for (v = vStart; v < vEnd; ++v) {
5136     PetscScalar *vcoords;
5137 
5138     PetscCall(DMLabelGetValue(bodyLabel, v, &bodyID));
5139     PetscCall(DMLabelGetValue(faceLabel, v, &faceID));
5140     PetscCall(DMLabelGetValue(edgeLabel, v, &edgeID));
5141     PetscCall(DMLabelGetValue(vertexLabel, v, &vertexID));
5142 
5143     // TODO Figure out why this is unknown sometimes
5144     if (bodyID < 0 && Nb == 1) bodyID = 0;
5145     PetscCheck(bodyID >= 0 && bodyID < Nb, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Body %" PetscInt_FMT " for vertex %" PetscInt_FMT " is not in [0, %d)", bodyID, v, Nb);
5146     body = bodies[bodyID];
5147 
5148     PetscCall(DMPlexPointLocalRef(cdm, v, coords, (void *)&vcoords));
5149     if (vertexID > 0) {
5150       /* Snap to Vertices */
5151       if (islite) {
5152         PetscCall(EGlite_objectBodyTopo(body, NODE, vertexID, &vertex));
5153         PetscCall(EGlite_evaluate(vertex, NULL, result));
5154       } else {
5155         PetscCall(EG_objectBodyTopo(body, NODE, vertexID, &vertex));
5156         PetscCall(EG_evaluate(vertex, NULL, result));
5157       }
5158       for (d = 0; d < cdim; ++d) vcoords[d] = result[d];
5159     } else if (edgeID > 0) {
5160       /* Snap to EDGE */
5161       params[0] = t_point[v - vStart];
5162       if (islite) {
5163         PetscCall(EGlite_objectBodyTopo(body, EDGE, edgeID, &edge));
5164         PetscCall(EGlite_evaluate(edge, params, result));
5165       } else {
5166         PetscCall(EG_objectBodyTopo(body, EDGE, edgeID, &edge));
5167         PetscCall(EG_evaluate(edge, params, result));
5168       }
5169       for (d = 0; d < cdim; ++d) vcoords[d] = result[d];
5170     } else if (faceID > 0) {
5171       /* Snap to FACE */
5172       params[0] = u_point[v - vStart];
5173       params[1] = v_point[v - vStart];
5174       if (islite) {
5175         PetscCall(EGlite_objectBodyTopo(body, FACE, faceID, &face));
5176         PetscCall(EGlite_evaluate(face, params, result));
5177       } else {
5178         PetscCall(EG_objectBodyTopo(body, FACE, faceID, &face));
5179         PetscCall(EG_evaluate(face, params, result));
5180       }
5181       for (d = 0; d < cdim; ++d) vcoords[d] = result[d];
5182     }
5183   }
5184   PetscCall(VecRestoreArrayWrite(coordinates, &coords));
5185   /* Clear out global coordinates */
5186   PetscCall(VecDestroy(&dm->coordinates[0].x));
5187 #endif
5188   PetscFunctionReturn(PETSC_SUCCESS);
5189 }
5190 
5191 /*@
5192   DMPlexInflateToGeomModel - Wrapper function allowing two methods for inflating refined meshes to the underlying geometric domain.
5193 
5194   Collective
5195 
5196   Input Parameters:
5197 + dm     - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5198 - useTUV - PetscBool indicating if the user would like to inflate the DMPlex to the underlying geometry
5199            using (t) for nodes on EDGEs and (u, v) for nodes on FACEs or using the nodes (x, y, z) coordinates
5200            and shortest distance routine.
5201             If useTUV = PETSC_TRUE, use the (t) or (u, v) parameters to inflate the DMPlex to the CAD geometry.
5202             If useTUV = PETSC_FALSE, use the nodes (x, y, z) coordinates and the shortest disctance routine.
5203 
5204   Notes:
5205   DM with nodal coordinates modified so that they lie on the EDGEs and FACEs of the underlying geometry.
5206 
5207   (t) and (u, v) parameters for all DMPlex nodes on EDGEs and FACEs are stored in arrays within PetscContainers attached to the DM.
5208   The containers have names "Point - Edge t Parameter", "Point - Face u Parameter", and "Point - Face v Parameter".
5209   The arrays are organized by Point 0-based ID (i.e. [v-vstart] as defined in the DMPlex.
5210 
5211   Level: intermediate
5212 
5213 .seealso: `DMPlexGetGeomModelTUV()`, `DMPlexInflateToGeomModelUseTUV()`, `DMPlexInflateToGeomModelUseXYZ()`
5214 @*/
5215 PetscErrorCode DMPlexInflateToGeomModel(DM dm, PetscBool useTUV) PeNS
5216 {
5217   PetscFunctionBeginHot;
5218   if (useTUV) {
5219     PetscCall(DMPlexGetGeomModelTUV(dm));
5220     PetscCall(DMPlexInflateToGeomModelUseTUV(dm));
5221   } else {
5222     PetscCall(DMPlexInflateToGeomModelUseXYZ(dm));
5223   }
5224   PetscFunctionReturn(PETSC_SUCCESS);
5225 }
5226 
5227 #ifdef PETSC_HAVE_EGADS
5228 /*@C
5229   DMPlexGetGeomModelBodies - Returns an array of `PetscGeom` BODY objects attached to the referenced geometric model entity as well as the number of BODYs.
5230 
5231   Collective
5232 
5233   Input Parameter:
5234 . dm - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5235 
5236   Output Parameters:
5237 + bodies    - Array of PetscGeom BODY objects referenced by the geometric model.
5238 - numBodies - Number of BODYs referenced by the geometric model. Also the size of **bodies array.
5239 
5240   Level: intermediate
5241 
5242 .seealso:
5243 @*/
5244 PetscErrorCode DMPlexGetGeomModelBodies(DM dm, PetscGeom **bodies, PetscInt *numBodies) PeNS
5245 {
5246   PetscFunctionBeginHot;
5247   PetscContainer modelObj;
5248   PetscBool      islite = PETSC_FALSE;
5249   ego            model, geom;
5250   int            oclass, mtype;
5251   int           *senses;
5252 
5253   /* Determine which type of EGADS model is attached to the DM */
5254   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5255   if (!modelObj) {
5256     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5257     islite = PETSC_TRUE;
5258   }
5259 
5260   // Get attached EGADS or EGADSlite model (pointer)
5261   PetscCall(PetscContainerGetPointer(modelObj, (void **)&model));
5262 
5263   if (islite) {
5264     PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, numBodies, bodies, &senses));
5265   } else {
5266     PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, numBodies, bodies, &senses));
5267   }
5268   PetscFunctionReturn(PETSC_SUCCESS);
5269 }
5270 
5271 /*@C
5272   DMPlexGetGeomModelBodyShells - Returns an array of `PetscGeom` SHELL objects attached to the referenced BODY geometric entity as well as the number of SHELLs.
5273 
5274   Collective
5275 
5276   Input Parameters:
5277 + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5278 - body - PetscGeom BODY object containing the SHELL objects of interest.
5279 
5280   Output Parameters:
5281 + shells    - Array of PetscGeom SHELL objects referenced by the PetscGeom BODY object
5282 - numShells - Number of SHELLs referenced by the PetscGeom BODY object. Also the size of **shells array.
5283 
5284   Level: intermediate
5285 
5286 .seealso:
5287 @*/
5288 PetscErrorCode DMPlexGetGeomModelBodyShells(DM dm, PetscGeom body, PetscGeom **shells, PetscInt *numShells) PeNS
5289 {
5290   PetscFunctionBeginHot;
5291   #ifdef PETSC_HAVE_EGADS
5292   PetscContainer modelObj;
5293   PetscBool      islite = PETSC_FALSE;
5294 
5295   /* Determine which type of EGADS model is attached to the DM */
5296   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5297   if (!modelObj) {
5298     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5299     islite = PETSC_TRUE;
5300   }
5301 
5302   if (islite) {
5303     PetscCall(EGlite_getBodyTopos(body, NULL, SHELL, numShells, shells));
5304   } else {
5305     PetscCall(EG_getBodyTopos(body, NULL, SHELL, numShells, shells));
5306   }
5307   #endif
5308   PetscFunctionReturn(PETSC_SUCCESS);
5309 }
5310 
5311 /*@C
5312   DMPlexGetGeomModelBodyFaces - Returns an array of `PetscGeom` FACE objects attached to the referenced BODY geometric entity as well as the number of FACEs.
5313 
5314   Collective
5315 
5316   Input Parameters:
5317 + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5318 - body - PetscGeom BODY object containing the FACE objects of interest.
5319 
5320   Output Parameters:
5321 + faces    - Array of PetscGeom FACE objects referenced by the PetscGeom BODY object
5322 - numFaces - Number of FACEs referenced by the PetscGeom BODY object. Also the size of **faces array.
5323 
5324   Level: intermediate
5325 
5326 .seealso:
5327 @*/
5328 PetscErrorCode DMPlexGetGeomModelBodyFaces(DM dm, PetscGeom body, PetscGeom **faces, PetscInt *numFaces) PeNS
5329 {
5330   PetscFunctionBeginHot;
5331   #ifdef PETSC_HAVE_EGADS
5332   PetscContainer modelObj;
5333   PetscBool      islite = PETSC_FALSE;
5334 
5335   /* Determine which type of EGADS model is attached to the DM */
5336   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5337   if (!modelObj) {
5338     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5339     islite = PETSC_TRUE;
5340   }
5341 
5342   if (islite) {
5343     PetscCall(EGlite_getBodyTopos(body, NULL, FACE, numFaces, faces));
5344   } else {
5345     PetscCall(EG_getBodyTopos(body, NULL, FACE, numFaces, faces));
5346   }
5347   #endif
5348   PetscFunctionReturn(PETSC_SUCCESS);
5349 }
5350 
5351 /*@C
5352   DMPlexGetGeomModelBodyLoops - Returns an array of `PetscGeom` Loop objects attached to the referenced BODY geometric entity as well as the number of LOOPs.
5353 
5354   Collective
5355 
5356   Input Parameters:
5357 + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5358 - body - PetscGeom BODY object containing the LOOP objects of interest.
5359 
5360   Output Parameters:
5361 + loops    - Array of PetscGeom FACE objects referenced by the PetscGeom SHELL object
5362 - numLoops - Number of LOOPs referenced by the PetscGeom BODY object. Also the size of **loops array.
5363 
5364   Level: intermediate
5365 
5366 .seealso:
5367 @*/
5368 PetscErrorCode DMPlexGetGeomModelBodyLoops(DM dm, PetscGeom body, PetscGeom **loops, PetscInt *numLoops) PeNS
5369 {
5370   PetscFunctionBeginHot;
5371   #ifdef PETSC_HAVE_EGADS
5372   PetscContainer modelObj;
5373   PetscBool      islite = PETSC_FALSE;
5374 
5375   /* Determine which type of EGADS model is attached to the DM */
5376   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5377   if (!modelObj) {
5378     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5379     islite = PETSC_TRUE;
5380   }
5381 
5382   if (islite) {
5383     PetscCall(EGlite_getBodyTopos(body, NULL, LOOP, numLoops, loops));
5384   } else {
5385     PetscCall(EG_getBodyTopos(body, NULL, LOOP, numLoops, loops));
5386   }
5387   #endif
5388   PetscFunctionReturn(PETSC_SUCCESS);
5389 }
5390 
5391 /*@C
5392   DMPlexGetGeomModelShellFaces - Returns an array of `PetscGeom` FACE objects attached to the referenced SHELL geometric entity as well as the number of FACEs.
5393 
5394   Collective
5395 
5396   Input Parameters:
5397 + dm    - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5398 . body  - PetscGeom BODY object containing the FACE objects of interest.
5399 - shell - PetscGeom SHELL object with FACEs of interest.
5400 
5401   Output Parameters:
5402 + faces    - Array of PetscGeom FACE objects referenced by the PetscGeom SHELL object
5403 - numFaces - Number of FACEs referenced by the PetscGeom SHELL object. Also the size of **faces array.
5404 
5405   Level: intermediate
5406 
5407 .seealso:
5408 @*/
5409 PetscErrorCode DMPlexGetGeomModelShellFaces(DM dm, PetscGeom body, PetscGeom shell, PetscGeom **faces, PetscInt *numFaces) PeNS
5410 {
5411   PetscFunctionBeginHot;
5412   #ifdef PETSC_HAVE_EGADS
5413   PetscContainer modelObj;
5414   PetscBool      islite = PETSC_FALSE;
5415 
5416   /* Determine which type of EGADS model is attached to the DM */
5417   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5418   if (!modelObj) {
5419     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5420     islite = PETSC_TRUE;
5421   }
5422 
5423   if (islite) {
5424     PetscCall(EGlite_getBodyTopos(body, shell, FACE, numFaces, faces));
5425   } else {
5426     PetscCall(EG_getBodyTopos(body, shell, FACE, numFaces, faces));
5427   }
5428   #endif
5429   PetscFunctionReturn(PETSC_SUCCESS);
5430 }
5431 
5432 /*@C
5433   DMPlexGetGeomModelFaceLoops - Returns an array of `PetscGeom` LOOP objects attached to the referenced FACE geometric entity as well as the number of LOOPs.
5434 
5435   Collective
5436 
5437   Input Parameters:
5438 + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5439 . body - PetscGeom BODY object containing the LOOP objects of interest.
5440 - face - PetscGeom FACE object with LOOPs of interest.
5441 
5442   Output Parameters:
5443 + loops    - Array of PetscGeom LOOP objects referenced by the PetscGeom FACE object
5444 - numLoops - Number of LOOPs referenced by the PetscGeom FACE object. Also the size of **loops array.
5445 
5446   Level: intermediate
5447 
5448 .seealso:
5449 @*/
5450 PetscErrorCode DMPlexGetGeomModelFaceLoops(DM dm, PetscGeom body, PetscGeom face, PetscGeom **loops, PetscInt *numLoops) PeNS
5451 {
5452   PetscFunctionBeginHot;
5453   #ifdef PETSC_HAVE_EGADS
5454   PetscContainer modelObj;
5455   PetscBool      islite = PETSC_FALSE;
5456 
5457   /* Determine which type of EGADS model is attached to the DM */
5458   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5459   if (!modelObj) {
5460     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5461     islite = PETSC_TRUE;
5462   }
5463 
5464   if (islite) {
5465     PetscCall(EGlite_getBodyTopos(body, face, LOOP, numLoops, loops));
5466   } else {
5467     PetscCall(EG_getBodyTopos(body, face, LOOP, numLoops, loops));
5468   }
5469   #endif
5470   PetscFunctionReturn(PETSC_SUCCESS);
5471 }
5472 
5473 /*@C
5474   DMPlexGetGeomModelFaceEdges - Returns an array of `PetscGeom` EDGE objects attached to the referenced FACE geometric entity as well as the number of EDGEs.
5475 
5476   Collective
5477 
5478   Input Parameters:
5479 + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5480 . body - PetscGeom Body object containing the EDGE objects of interest.
5481 - face - PetscGeom FACE object with EDGEs of interest.
5482 
5483   Output Parameters:
5484 + edges    - Array of PetscGeom EDGE objects referenced by the PetscGeom FACE object
5485 - numEdges - Number of EDGEs referenced by the PetscGeom FACE object. Also the size of **edges array.
5486 
5487   Level: intermediate
5488 
5489 .seealso:
5490 @*/
5491 PetscErrorCode DMPlexGetGeomModelFaceEdges(DM dm, PetscGeom body, PetscGeom face, PetscGeom **edges, PetscInt *numEdges) PeNS
5492 {
5493   PetscFunctionBeginHot;
5494   #ifdef PETSC_HAVE_EGADS
5495   PetscContainer modelObj;
5496   PetscBool      islite = PETSC_FALSE;
5497 
5498   /* Determine which type of EGADS model is attached to the DM */
5499   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5500   if (!modelObj) {
5501     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5502     islite = PETSC_TRUE;
5503   }
5504 
5505   if (islite) {
5506     PetscCall(EGlite_getBodyTopos(body, face, EDGE, numEdges, edges));
5507   } else {
5508     PetscCall(EG_getBodyTopos(body, face, EDGE, numEdges, edges));
5509   }
5510   #endif
5511   PetscFunctionReturn(PETSC_SUCCESS);
5512 }
5513 
5514 /*@C
5515   DMPlexGetGeomModelBodyEdges - Returns an array of `PetscGeom` EDGE objects attached to the referenced BODY geometric entity as well as the number of EDGEs.
5516 
5517   Collective
5518 
5519   Input Parameters:
5520 + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5521 - body - PetscGeom body object of interest.
5522 
5523   Output Parameters:
5524 + edges    - Array of PetscGeom EDGE objects referenced by the PetscGeom BODY object
5525 - numEdges - Number of EDGEs referenced by the PetscGeom BODY object. Also the size of **edges array.
5526 
5527   Level: intermediate
5528 
5529 .seealso:
5530 @*/
5531 PetscErrorCode DMPlexGetGeomModelBodyEdges(DM dm, PetscGeom body, PetscGeom **edges, PetscInt *numEdges) PeNS
5532 {
5533   PetscFunctionBeginHot;
5534   #ifdef PETSC_HAVE_EGADS
5535   PetscContainer modelObj;
5536   PetscBool      islite = PETSC_FALSE;
5537 
5538   /* Determine which type of EGADS model is attached to the DM */
5539   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5540   if (!modelObj) {
5541     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5542     islite = PETSC_TRUE;
5543   }
5544 
5545   if (islite) {
5546     PetscCall(EGlite_getBodyTopos(body, NULL, EDGE, numEdges, edges));
5547   } else {
5548     PetscCall(EG_getBodyTopos(body, NULL, EDGE, numEdges, edges));
5549   }
5550   #endif
5551   PetscFunctionReturn(PETSC_SUCCESS);
5552 }
5553 
5554 /*@C
5555   DMPlexGetGeomModelBodyNodes - Returns an array of `PetscGeom` NODE objects attached to the referenced BODY geometric entity as well as the number of NODES.
5556 
5557   Collective
5558 
5559   Input Parameters:
5560 + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5561 - body - PetscGeom body object of interest.
5562 
5563   Output Parameters:
5564 + nodes    - Array of PetscGeom NODE objects referenced by the PetscGeom BODY object
5565 - numNodes - Number of NODEs referenced by the PetscGeom BODY object. Also the size of **nodes array.
5566 
5567   Level: intermediate
5568 
5569 .seealso:
5570 @*/
5571 PetscErrorCode DMPlexGetGeomModelBodyNodes(DM dm, PetscGeom body, PetscGeom **nodes, PetscInt *numNodes) PeNS
5572 {
5573   PetscFunctionBeginHot;
5574   #ifdef PETSC_HAVE_EGADS
5575   PetscContainer modelObj;
5576   PetscBool      islite = PETSC_FALSE;
5577 
5578   /* Determine which type of EGADS model is attached to the DM */
5579   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5580   if (!modelObj) {
5581     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5582     islite = PETSC_TRUE;
5583   }
5584 
5585   if (islite) {
5586     PetscCall(EGlite_getBodyTopos(body, NULL, NODE, numNodes, nodes));
5587   } else {
5588     PetscCall(EG_getBodyTopos(body, NULL, NODE, numNodes, nodes));
5589   }
5590   #endif
5591   PetscFunctionReturn(PETSC_SUCCESS);
5592 }
5593 
5594 /*@C
5595   DMPlexGetGeomModelEdgeNodes - Returns an array of `PetscGeom` NODE objects attached to the referenced EDGE geometric entity as well as the number of NODES.
5596 
5597   Collective
5598 
5599   Input Parameters:
5600 + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5601 . body - PetscGeom body object containing the EDGE object of interest.
5602 - edge - PetscGeom EDGE object with NODEs of interest.
5603 
5604   Output Parameters:
5605 + nodes    - Array of PetscGeom NODE objects referenced by the PetscGeom EDGE object
5606 - numNodes - Number of Nodes referenced by the PetscGeom EDGE object. Also the size of **nodes array.
5607 
5608   Level: intermediate
5609 
5610 .seealso:
5611 @*/
5612 PetscErrorCode DMPlexGetGeomModelEdgeNodes(DM dm, PetscGeom body, PetscGeom edge, PetscGeom **nodes, PetscInt *numNodes) PeNS
5613 {
5614   PetscFunctionBeginHot;
5615   #ifdef PETSC_HAVE_EGADS
5616   PetscContainer modelObj;
5617   PetscBool      islite = PETSC_FALSE;
5618 
5619   /* Determine which type of EGADS model is attached to the DM */
5620   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5621   if (!modelObj) {
5622     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5623     islite = PETSC_TRUE;
5624   }
5625 
5626   if (islite) {
5627     PetscCall(EGlite_getBodyTopos(body, edge, NODE, numNodes, nodes));
5628   } else {
5629     PetscCall(EG_getBodyTopos(body, edge, NODE, numNodes, nodes));
5630   }
5631   #endif
5632   PetscFunctionReturn(PETSC_SUCCESS);
5633 }
5634 
5635 /*@C
5636   DMPlexGetGeomID - Returns ID number of the entity in the geometric (CAD) model
5637 
5638   Collective
5639 
5640   Input Parameters:
5641 + dm      - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5642 . body    - PetscGeom body object containing the lower level entity the ID number is being requested.
5643 - topoObj - PetscGeom SHELL, FACE, LOOP, EDGE, or NODE object for which ID number is being requested.
5644 
5645   Output Parameter:
5646 . id - ID number of the entity
5647 
5648   Level: intermediate
5649 
5650 .seealso:
5651 @*/
5652 PetscErrorCode DMPlexGetGeomID(DM dm, PetscGeom body, PetscGeom topoObj, PetscInt *id) PeNS
5653 {
5654   PetscFunctionBeginHot;
5655   #ifdef PETSC_HAVE_EGADS
5656   PetscContainer modelObj;
5657   PetscBool      islite = PETSC_FALSE;
5658   int            topoID;
5659 
5660   /* Determine which type of EGADS model is attached to the DM */
5661   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5662   if (!modelObj) {
5663     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5664     islite = PETSC_TRUE;
5665   }
5666 
5667   // Get Topology Object's ID
5668   if (islite) {
5669     topoID = EGlite_indexBodyTopo(body, topoObj);
5670   } else {
5671     topoID = EG_indexBodyTopo(body, topoObj);
5672   }
5673 
5674   *id = topoID;
5675   #endif
5676   PetscFunctionReturn(PETSC_SUCCESS);
5677 }
5678 
5679 /*@C
5680   DMPlexGetGeomObject - Returns Geometry Object using the objects ID in the geometric (CAD) model
5681 
5682   Collective
5683 
5684   Input Parameters:
5685 + dm       - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5686 . body     - PetscGeom body object containing the lower level entity the referenced by the ID.
5687 . geomType - Keyword SHELL, FACE, LOOP, EDGE, or NODE of the geometry type for which ID number is being requested.
5688 - geomID   - ID number of the geometry entity being requested.
5689 
5690   Output Parameter:
5691 . geomObj - Geometry Object referenced by the ID number requested.
5692 
5693   Level: intermediate
5694 
5695 .seealso:
5696 @*/
5697 PetscErrorCode DMPlexGetGeomObject(DM dm, PetscGeom body, PetscInt geomType, PetscInt geomID, PetscGeom *geomObj) PeNS
5698 {
5699   PetscFunctionBeginHot;
5700   #ifdef PETSC_HAVE_EGADS
5701   PetscContainer modelObj;
5702   PetscBool      islite = PETSC_FALSE;
5703 
5704   /* Determine which type of EGADS model is attached to the DM */
5705   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5706   if (!modelObj) {
5707     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5708     islite = PETSC_TRUE;
5709   }
5710 
5711   // Get Topology Object's ID
5712   if (islite) {
5713     PetscCall(EGlite_objectBodyTopo(body, geomType, geomID, geomObj));
5714   } else {
5715     PetscCall(EG_objectBodyTopo(body, geomType, geomID, geomObj));
5716   }
5717   #endif
5718   PetscFunctionReturn(PETSC_SUCCESS);
5719 }
5720 
5721 /*@C
5722   DMPlexGetGeomFaceNumOfControlPoints - Returns the total number of Control Points (and associated Weights) defining a FACE of a Geometry
5723 
5724   Not collective
5725 
5726   Input Parameters:
5727 + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5728 - face - PetscGeom FACE object
5729 
5730   Output Parameter:
5731 . numCntrlPnts - Number of Control Points (and Weights) defining the FACE
5732 
5733   Level: intermediate
5734 
5735 .seealso:
5736 @*/
5737 PetscErrorCode DMPlexGetGeomFaceNumOfControlPoints(DM dm, PetscGeom face, PetscInt *numCntrlPnts) PeNS
5738 {
5739   PetscFunctionBeginHot;
5740   #ifdef PETSC_HAVE_EGADS
5741   PetscContainer modelObj;
5742   PetscBool      islite = PETSC_FALSE;
5743   PetscGeom      geom, gRef;
5744   PetscGeom     *lobjs;
5745   int            Nl, oclass, mtype, goclass, gmtype;
5746   int           *lsenses, *gpinfo;
5747   double        *gprv;
5748 
5749   /* Determine which type of EGADS model is attached to the DM */
5750   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5751   if (!modelObj) {
5752     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5753     islite = PETSC_TRUE;
5754   }
5755 
5756   // Get Total Number of Control Points on FACE
5757   if (islite) {
5758     PetscCall(EGlite_getTopology(face, &geom, &oclass, &mtype, NULL, &Nl, &lobjs, &lsenses));
5759     PetscCall(EGlite_getGeometry(geom, &goclass, &gmtype, &gRef, &gpinfo, &gprv));
5760   } else {
5761     PetscCall(EG_getTopology(face, &geom, &oclass, &mtype, NULL, &Nl, &lobjs, &lsenses));
5762     PetscCall(EG_getGeometry(geom, &goclass, &gmtype, &gRef, &gpinfo, &gprv));
5763   }
5764 
5765   *numCntrlPnts = gpinfo[2] * gpinfo[5];
5766   #endif
5767   PetscFunctionReturn(PETSC_SUCCESS);
5768 }
5769 
5770 /*@C
5771   DMPlexGetGeomBodyMassProperties - Returns the Volume, Surface Area, Center of Gravity, and Inertia about the Body's Center of Gravity
5772 
5773   Not collective
5774 
5775   Input Parameters:
5776 + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5777 - body - PetscGeom BODY object
5778 
5779   Output Parameters:
5780 + volume           - Volume of the CAD Body attached to the DM Plex
5781 . surfArea         - Surface Area of the CAD Body attached to the DM Plex
5782 . centerOfGravity  - Array with the Center of Gravity coordinates of the CAD Body attached to the DM Plex [x, y, z]
5783 . COGszie          - Size of centerOfGravity[] Array
5784 . inertiaMatrixCOG - Array containing the Inertia about the Body's Center of Gravity [Ixx, Ixy, Ixz, Iyx, Iyy, Iyz, Izx, Izy, Izz]
5785 - IMCOGsize        - Size of inertiaMatrixCOG[] Array
5786 
5787   Level: intermediate
5788 
5789 .seealso:
5790 @*/
5791 PetscErrorCode DMPlexGetGeomBodyMassProperties(DM dm, PetscGeom body, PetscScalar *volume, PetscScalar *surfArea, PetscScalar **centerOfGravity, PetscInt *COGsize, PetscScalar **inertiaMatrixCOG, PetscInt *IMCOGsize) PeNS
5792 {
5793   PetscFunctionBeginHot;
5794   #ifdef PETSC_HAVE_EGADS
5795   PetscContainer modelObj;
5796   PetscBool      islite = PETSC_FALSE;
5797   PetscScalar    geomData[14];
5798 
5799   /* Determine which type of EGADS model is attached to the DM */
5800   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5801   if (!modelObj) {
5802     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5803     islite = PETSC_TRUE;
5804     PetscCheck(modelObj, PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot provide geometric mass properties for geometries defined by EGADSlite (.egadslite)! Please use another geometry file format STEP, IGES, EGADS or BRep");
5805   }
5806 
5807   if (islite) {
5808     PetscCall(PetscPrintf(PETSC_COMM_SELF, " WARNING!! This functionality is not supported for EGADSlite files. \n"));
5809     PetscCall(PetscPrintf(PETSC_COMM_SELF, " All returned values are equal to 0 \n"));
5810   } else {
5811     PetscCall(EG_getMassProperties(body, geomData));
5812   }
5813 
5814   PetscCall(PetscMalloc2(3, centerOfGravity, 9, inertiaMatrixCOG));
5815 
5816   if (!islite) {
5817     *volume   = geomData[0];
5818     *surfArea = geomData[1];
5819     for (int ii = 2; ii < 5; ++ii) { (*centerOfGravity)[ii - 2] = geomData[ii]; }
5820     *COGsize = 3;
5821     for (int ii = 5; ii < 14; ++ii) { (*inertiaMatrixCOG)[ii - 5] = geomData[ii]; }
5822     *IMCOGsize = 9;
5823   } else {
5824     *volume   = 0.;
5825     *surfArea = 0.;
5826     for (int ii = 2; ii < 5; ++ii) { (*centerOfGravity)[ii - 2] = 0.; }
5827     *COGsize = 0;
5828     for (int ii = 5; ii < 14; ++ii) { (*inertiaMatrixCOG)[ii - 5] = 0.; }
5829     *IMCOGsize = 0;
5830   }
5831   #endif
5832   PetscFunctionReturn(PETSC_SUCCESS);
5833 }
5834 
5835 PetscErrorCode DMPlexRestoreGeomBodyMassProperties(DM dm, PetscGeom body, PetscScalar *volume, PetscScalar *surfArea, PetscScalar **centerOfGravity, PetscInt *COGsize, PetscScalar **inertiaMatrixCOG, PetscInt *IMCOGsize) PeNS
5836 {
5837   PetscFunctionBegin;
5838   PetscCall(PetscFree2(*centerOfGravity, *inertiaMatrixCOG));
5839   PetscFunctionReturn(PETSC_SUCCESS);
5840 }
5841 
5842 /*@C
5843   DMPlexFreeGeomObject - Frees PetscGeom Objects
5844 
5845   Not collective
5846 
5847   Input Parameters:
5848 + dm      - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5849 - geomObj - PetscGeom object
5850 
5851   Level: intermediate
5852 
5853 .seealso:
5854 @*/
5855 PetscErrorCode DMPlexFreeGeomObject(DM dm, PetscGeom *geomObj) PeNS
5856 {
5857   PetscFunctionBeginHot;
5858   #ifdef PETSC_HAVE_EGADS
5859   PetscContainer modelObj;
5860   PetscBool      islite = PETSC_FALSE;
5861 
5862   /* Determine which type of EGADS model is attached to the DM */
5863   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5864   if (!modelObj) {
5865     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5866     islite = PETSC_TRUE;
5867   }
5868 
5869   if (islite) {
5870     EGlite_free(geomObj);
5871   } else {
5872     EG_free(geomObj);
5873   }
5874   #endif
5875   PetscFunctionReturn(PETSC_SUCCESS);
5876 }
5877 
5878 /*@C
5879   DMPlexGetGeomCntrlPntAndWeightData - Gets Control Point and Associated Weight Data for the Geometry attached to the DMPlex
5880 
5881   Not collective
5882 
5883   Input Parameter:
5884 . dm - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5885 
5886   Output Parameters:
5887 + cpHashTable       - Hash Table containing the relationship between FACE ID and Control Point IDs.
5888 . cpCoordDataLength - Length of cpCoordData Array.
5889 . cpCoordData       - Array holding the Geometry Control Point Coordinate Data.
5890 . maxNumEquiv       - Maximum Number of Equivalent Control Points (Control Points with the same coordinates but different IDs).
5891 . cpEquiv           - Matrix with a size(Number of Control Points, Number or Control Points) which stores a value of 1.0 in locations where Control Points with different IDS (row or column) have the same coordinates
5892 . wHashTable        - Hash Table containing the relationship between FACE ID and Control Point Weight.
5893 . wDataLength       - Length of wData Array.
5894 - wData             - Array holding the Weight for an associated Geometry Control Point.
5895 
5896   Note:
5897   Must Call DMPLexGeomDataAndGrads() before calling this function.
5898 
5899   Level: intermediate
5900 
5901 .seealso:
5902 @*/
5903 PetscErrorCode DMPlexGetGeomCntrlPntAndWeightData(DM dm, PetscHMapI *cpHashTable, PetscInt *cpCoordDataLength, PetscScalar **cpCoordData, PetscInt *maxNumEquiv, Mat *cpEquiv, PetscHMapI *wHashTable, PetscInt *wDataLength, PetscScalar **wData) PeNS
5904 {
5905   PetscContainer modelObj, cpHashTableObj, wHashTableObj, cpCoordDataLengthObj, wDataLengthObj, maxNumRelateObj;
5906   Vec            cntrlPtCoordsVec, cntrlPtWeightsVec;
5907   PetscInt      *cpCoordDataLengthPtr, *wDataLengthPtr, *maxNumEquivPtr;
5908   PetscHMapI     cpHashTableTemp, wHashTableTemp;
5909 
5910   PetscFunctionBeginHot;
5911   /* Determine which type of EGADS model is attached to the DM */
5912   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5913   if (!modelObj) { PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj)); }
5914 
5915   if (!modelObj) { PetscFunctionReturn(PETSC_SUCCESS); }
5916 
5917   // Look to see if DM has Container for Geometry Control Point Data
5918   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Hash Table", (PetscObject *)&cpHashTableObj));
5919   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Coordinates", (PetscObject *)&cntrlPtCoordsVec));
5920   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Coordinate Data Length", (PetscObject *)&cpCoordDataLengthObj));
5921   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weights Hash Table", (PetscObject *)&wHashTableObj));
5922   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight Data", (PetscObject *)&cntrlPtWeightsVec));
5923   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight Data Length", (PetscObject *)&wDataLengthObj));
5924   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Equivalency Matrix", (PetscObject *)cpEquiv));
5925   PetscCall(PetscObjectQuery((PetscObject)dm, "Maximum Number Control Point Equivalency", (PetscObject *)&maxNumRelateObj));
5926 
5927   // Get attached EGADS model Control Point and Weights Hash Tables and Data Arrays (pointer)
5928   PetscCall(PetscContainerGetPointer(cpHashTableObj, (void **)&cpHashTableTemp));
5929   PetscCall(PetscContainerGetPointer(cpCoordDataLengthObj, (void **)&cpCoordDataLengthPtr));
5930   PetscCall(PetscContainerGetPointer(wHashTableObj, (void **)&wHashTableTemp));
5931   PetscCall(PetscContainerGetPointer(wDataLengthObj, (void **)&wDataLengthPtr));
5932   PetscCall(PetscContainerGetPointer(maxNumRelateObj, (void **)&maxNumEquivPtr));
5933 
5934   *cpCoordDataLength = *cpCoordDataLengthPtr;
5935   *wDataLength       = *wDataLengthPtr;
5936   *maxNumEquiv       = *maxNumEquivPtr;
5937   *cpHashTable       = cpHashTableTemp;
5938   *wHashTable        = wHashTableTemp;
5939   PetscCall(VecGetArrayWrite(cntrlPtCoordsVec, cpCoordData));
5940   PetscCall(VecGetArrayWrite(cntrlPtWeightsVec, wData));
5941   PetscFunctionReturn(PETSC_SUCCESS);
5942 }
5943 
5944 PetscErrorCode DMPlexRestoreGeomCntrlPntAndWeightData(DM dm, PetscHMapI *cpHashTable, PetscInt *cpCoordDataLength, PetscScalar **cpCoordData, PetscInt *maxNumEquiv, Mat *cpEquiv, PetscHMapI *wHashTable, PetscInt *wDataLength, PetscScalar **wData)
5945 {
5946   Vec cntrlPtCoordsVec, cntrlPtWeightsVec;
5947 
5948   PetscFunctionBeginHot;
5949   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Coordinates", (PetscObject *)&cntrlPtCoordsVec));
5950   PetscCall(VecRestoreArrayWrite(cntrlPtCoordsVec, cpCoordData));
5951   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight Data", (PetscObject *)&cntrlPtWeightsVec));
5952   PetscCall(VecRestoreArrayWrite(cntrlPtWeightsVec, wData));
5953   PetscFunctionReturn(PETSC_SUCCESS);
5954 }
5955 
5956 /*@C
5957   DMPlexGetGeomGradData - Gets Point, Surface and Volume Gradients with respect to changes in Control Points and their associated Weights for the Geometry attached to the DMPlex .
5958 
5959   Not collective
5960 
5961   Input Parameter:
5962 . dm - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5963 
5964   Output Parameters:
5965 + cpSurfGradHashTable - Hash Table Relating the Control Point ID to the the Row in the cpSurfGrad Matrix
5966 . cpSurfGrad          - Matrix containing the Surface Gradient with respect to the Control Point Data. Data is ranged where the Row corresponds to Control Point ID and the Columns are associated with the Geometric FACE.
5967 . cpArraySize         - The size of arrays gradSACP and gradVolCP and is equal to 3 * total number of Control Points in the Geometry
5968 . gradSACP            - Array containing the Surface Area Gradient with respect to Control Point Data. Data is arranged by Control Point ID * 3 where 3 is for the coordinate dimension.
5969 . gradVolCP           - Array containing the Volume Gradient with respect to Control Point Data. Data is arranged by Control Point ID * 3 where 3 is for the coordinate dimension.
5970 . wArraySize          - The size of arrayws gradSAW and gradVolW and is equal to the total number of Control Points in the Geometry.
5971 . gradSAW             - Array containing the Surface Area Gradient with respect to Control Point Weight. Data is arranged by Control Point ID.
5972 - gradVolW            - Array containing the Volume Gradient with respect to Control Point Weight. Data is arranged by Control Point ID.
5973 
5974   Notes:
5975   Must Call DMPLexGeomDataAndGrads() before calling this function.
5976 
5977   gradVolCP and gradVolW are only available when DMPlexGeomDataAndGrads() is called with fullGeomGrad = PETSC_TRUE.
5978 
5979   Level: intermediate
5980 
5981 .seealso: DMPlexGeomDataAndGrads
5982 @*/
5983 PetscErrorCode DMPlexGetGeomGradData(DM dm, PetscHMapI *cpSurfGradHashTable, Mat *cpSurfGrad, PetscInt *cpArraySize, PetscScalar **gradSACP, PetscScalar **gradVolCP, PetscInt *wArraySize, PetscScalar **gradSAW, PetscScalar **gradVolW)
5984 {
5985   PetscContainer modelObj, cpSurfGradHashTableObj, cpArraySizeObj, wArraySizeObj;
5986   Vec            gradSACPVec, gradVolCPVec, gradSAWVec, gradVolWVec;
5987   PetscInt      *cpArraySizePtr, *wArraySizePtr;
5988   PetscHMapI     cpSurfGradHashTableTemp;
5989 
5990   PetscFunctionBeginHot;
5991   /* Determine which type of EGADS model is attached to the DM */
5992   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5993   if (!modelObj) { PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj)); }
5994 
5995   if (!modelObj) { PetscFunctionReturn(PETSC_SUCCESS); }
5996 
5997   // Look to see if DM has Container for Geometry Control Point Data
5998   PetscCall(PetscObjectQuery((PetscObject)dm, "Surface Gradient Hash Table", (PetscObject *)&cpSurfGradHashTableObj));
5999   PetscCall(PetscObjectQuery((PetscObject)dm, "Surface Gradient Matrix", (PetscObject *)cpSurfGrad));
6000   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Coordinate Data Length", (PetscObject *)&cpArraySizeObj));
6001   PetscCall(PetscObjectQuery((PetscObject)dm, "Surface Area Control Point Gradient", (PetscObject *)&gradSACPVec));
6002   PetscCall(PetscObjectQuery((PetscObject)dm, "Volume Control Point Gradient", (PetscObject *)&gradVolCPVec));
6003   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight Data Length", (PetscObject *)&wArraySizeObj));
6004   PetscCall(PetscObjectQuery((PetscObject)dm, "Surface Area Weights Gradient", (PetscObject *)&gradSAWVec));
6005   PetscCall(PetscObjectQuery((PetscObject)dm, "Volume Weights Gradient", (PetscObject *)&gradVolWVec));
6006 
6007   // Get attached EGADS model Control Point and Weights Hash Tables and Data Arrays (pointer)
6008   if (cpSurfGradHashTableObj) {
6009     PetscCall(PetscContainerGetPointer(cpSurfGradHashTableObj, (void **)&cpSurfGradHashTableTemp));
6010     *cpSurfGradHashTable = cpSurfGradHashTableTemp;
6011   }
6012 
6013   if (cpArraySizeObj) {
6014     PetscCall(PetscContainerGetPointer(cpArraySizeObj, (void **)&cpArraySizePtr));
6015     *cpArraySize = *cpArraySizePtr;
6016   }
6017 
6018   if (gradSACPVec) PetscCall(VecGetArrayWrite(gradSACPVec, gradSACP));
6019   if (gradVolCPVec) PetscCall(VecGetArrayWrite(gradVolCPVec, gradVolCP));
6020   if (gradSAWVec) PetscCall(VecGetArrayWrite(gradSAWVec, gradSAW));
6021   if (gradVolWVec) PetscCall(VecGetArrayWrite(gradVolWVec, gradVolW));
6022 
6023   if (wArraySizeObj) {
6024     PetscCall(PetscContainerGetPointer(wArraySizeObj, (void **)&wArraySizePtr));
6025     *wArraySize = *wArraySizePtr;
6026   }
6027   PetscFunctionReturn(PETSC_SUCCESS);
6028 }
6029 
6030 PetscErrorCode DMPlexRestoreGeomGradData(DM dm, PetscHMapI *cpSurfGradHashTable, Mat *cpSurfGrad, PetscInt *cpArraySize, PetscScalar **gradSACP, PetscScalar **gradVolCP, PetscInt *wArraySize, PetscScalar **gradSAW, PetscScalar **gradVolW)
6031 {
6032   Vec gradSACPVec, gradVolCPVec, gradSAWVec, gradVolWVec;
6033 
6034   PetscFunctionBegin;
6035   PetscCall(PetscObjectQuery((PetscObject)dm, "Surface Area Control Point Gradient", (PetscObject *)&gradSACPVec));
6036   PetscCall(PetscObjectQuery((PetscObject)dm, "Volume Control Point Gradient", (PetscObject *)&gradVolCPVec));
6037   PetscCall(PetscObjectQuery((PetscObject)dm, "Surface Area Weights Gradient", (PetscObject *)&gradSAWVec));
6038   PetscCall(PetscObjectQuery((PetscObject)dm, "Volume Weights Gradient", (PetscObject *)&gradVolWVec));
6039 
6040   if (gradSACPVec) PetscCall(VecRestoreArrayWrite(gradSACPVec, gradSACP));
6041   if (gradVolCPVec) PetscCall(VecRestoreArrayWrite(gradVolCPVec, gradVolCP));
6042   if (gradSAWVec) PetscCall(VecRestoreArrayWrite(gradSAWVec, gradSAW));
6043   if (gradVolWVec) PetscCall(VecRestoreArrayWrite(gradVolWVec, gradVolW));
6044   PetscFunctionReturn(PETSC_SUCCESS);
6045 }
6046 
6047 /*@C
6048   DMPlexGetGeomCntrlPntMaps - Gets arrays which maps Control Point IDs to their associated Geometry FACE, EDGE, and VERTEX.
6049 
6050   Not collective
6051 
6052   Input Parameter:
6053 . dm - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
6054 
6055   Output Parameters:
6056 + numCntrlPnts            - Number of Control Points defining the Geometry attached to the DMPlex
6057 . cntrlPntFaceMap         - Array containing the FACE ID for the Control Point. Array index corresponds to Control Point ID.
6058 . cntrlPntWeightFaceMap   - Array containing the FACE ID for the Control Point Weight. Array index corresponds to Control Point ID.
6059 . cntrlPntEdgeMap         - Array containing the EDGE ID for the Control Point. Array index corresponds to Control Point ID.
6060 . cntrlPntWeightEdgeMap   - Array containing the EDGE ID for the Control Point Weight. Array index corresponds to Control Point ID.
6061 . cntrlPntVertexMap       - Array containing the VERTEX ID for the Control Point. Array index corresponds to Control Point ID.
6062 - cntrlPntWeightVertexMap - Array containing the VERTEX ID for the Control Point Weight. Array index corresponds to Control Point ID.
6063 
6064   Note:
6065   Arrays are initialized to -1. Array elements with a -1 value indicates that the Control Point or Control Point Weight not associated with the referenced Geometric entity in the array name.
6066 
6067   Level: intermediate
6068 
6069 .seealso: DMPlexGeomDataAndGrads
6070 @*/
6071 PetscErrorCode DMPlexGetGeomCntrlPntMaps(DM dm, PetscInt *numCntrlPnts, PetscInt **cntrlPntFaceMap, PetscInt **cntrlPntWeightFaceMap, PetscInt **cntrlPntEdgeMap, PetscInt **cntrlPntWeightEdgeMap, PetscInt **cntrlPntVertexMap, PetscInt **cntrlPntWeightVertexMap)
6072 {
6073   PetscFunctionBeginHot;
6074   #ifdef PETSC_HAVE_EGADS
6075   PetscContainer modelObj, numCntrlPntsObj, cntrlPntFaceMapObj, cntrlPntWeightFaceMapObj, cntrlPntEdgeMapObj, cntrlPntWeightEdgeMapObj, cntrlPntVertexMapObj, cntrlPntWeightVertexMapObj;
6076   PetscInt      *numCntrlPntsPtr, *cntrlPntFaceMapPtr, *cntrlPntWeightFaceMapPtr, *cntrlPntEdgeMapPtr, *cntrlPntWeightEdgeMapPtr, *cntrlPntVertexMapPtr, *cntrlPntWeightVertexMapPtr;
6077 
6078   /* Determine which type of EGADS model is attached to the DM */
6079   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
6080   if (!modelObj) { PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj)); }
6081 
6082   if (!modelObj) { PetscFunctionReturn(PETSC_SUCCESS); }
6083 
6084   // Look to see if DM has Container for Geometry Control Point Data
6085   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight Data Length", (PetscObject *)&numCntrlPntsObj));
6086   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point - Face Map", (PetscObject *)&cntrlPntFaceMapObj));
6087   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight - Face Map", (PetscObject *)&cntrlPntWeightFaceMapObj));
6088   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point - Edge Map", (PetscObject *)&cntrlPntEdgeMapObj));
6089   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight - Edge Map", (PetscObject *)&cntrlPntWeightEdgeMapObj));
6090   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point - Vertex Map", (PetscObject *)&cntrlPntVertexMapObj));
6091   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight - Vertex Map", (PetscObject *)&cntrlPntWeightVertexMapObj));
6092 
6093   // Get attached EGADS model Control Point and Weights Hash Tables and Data Arrays (pointer)
6094   if (numCntrlPntsObj) {
6095     PetscCall(PetscContainerGetPointer(numCntrlPntsObj, (void **)&numCntrlPntsPtr));
6096     *numCntrlPnts = *numCntrlPntsPtr;
6097   }
6098 
6099   if (cntrlPntFaceMapObj) {
6100     PetscCall(PetscContainerGetPointer(cntrlPntFaceMapObj, (void **)&cntrlPntFaceMapPtr));
6101     *cntrlPntFaceMap = cntrlPntFaceMapPtr;
6102   }
6103 
6104   if (cntrlPntWeightFaceMapObj) {
6105     PetscCall(PetscContainerGetPointer(cntrlPntWeightFaceMapObj, (void **)&cntrlPntWeightFaceMapPtr));
6106     *cntrlPntWeightFaceMap = cntrlPntWeightFaceMapPtr;
6107   }
6108 
6109   if (cntrlPntEdgeMapObj) {
6110     PetscCall(PetscContainerGetPointer(cntrlPntEdgeMapObj, (void **)&cntrlPntEdgeMapPtr));
6111     *cntrlPntEdgeMap = cntrlPntEdgeMapPtr;
6112   }
6113 
6114   if (cntrlPntWeightEdgeMapObj) {
6115     PetscCall(PetscContainerGetPointer(cntrlPntWeightEdgeMapObj, (void **)&cntrlPntWeightEdgeMapPtr));
6116     *cntrlPntWeightEdgeMap = cntrlPntWeightEdgeMapPtr;
6117   }
6118 
6119   if (cntrlPntVertexMapObj) {
6120     PetscCall(PetscContainerGetPointer(cntrlPntVertexMapObj, (void **)&cntrlPntVertexMapPtr));
6121     *cntrlPntVertexMap = cntrlPntVertexMapPtr;
6122   }
6123 
6124   if (cntrlPntWeightVertexMapObj) {
6125     PetscCall(PetscContainerGetPointer(cntrlPntWeightVertexMapObj, (void **)&cntrlPntWeightVertexMapPtr));
6126     *cntrlPntWeightVertexMap = cntrlPntWeightVertexMapPtr;
6127   }
6128 
6129   #endif
6130   PetscFunctionReturn(PETSC_SUCCESS);
6131 }
6132 
6133 #endif
6134