xref: /petsc/src/dm/impls/plex/plexegads.c (revision d5def619400c53ceafc7498a3d382ce0d1e29f7d)
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 derviatives, 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 infor 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 Plex
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 Plex
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 Plex 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)
2479 {
2480 #if defined(PETSC_HAVE_EGADS)
2481   /* EGADS Variables */
2482   ego    model, geom, body, face, edge, vertex;
2483   ego   *bodies;
2484   int    Nb, oclass, mtype, *senses;
2485   double result[4];
2486   /* PETSc Variables */
2487   DM             cdm;
2488   PetscContainer modelObj;
2489   DMLabel        bodyLabel, faceLabel, edgeLabel, vertexLabel;
2490   Vec            coordinates;
2491   PetscScalar   *coords;
2492   PetscInt       bodyID, faceID, edgeID, vertexID;
2493   PetscInt       cdim, d, vStart, vEnd, v;
2494   PetscBool      islite = PETSC_FALSE;
2495 #endif
2496 
2497   PetscFunctionBegin;
2498 #if defined(PETSC_HAVE_EGADS)
2499   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
2500   if (!modelObj) {
2501     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
2502     islite = PETSC_TRUE;
2503   }
2504   if (!modelObj) PetscFunctionReturn(PETSC_SUCCESS);
2505   PetscCall(DMGetCoordinateDim(dm, &cdim));
2506   PetscCall(DMGetCoordinateDM(dm, &cdm));
2507   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
2508   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
2509   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
2510   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
2511   PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));
2512 
2513   PetscCall(PetscContainerGetPointer(modelObj, (void **)&model));
2514 
2515   if (islite) {
2516     PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
2517   } else {
2518     PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
2519   }
2520 
2521   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
2522   PetscCall(VecGetArrayWrite(coordinates, &coords));
2523   for (v = vStart; v < vEnd; ++v) {
2524     PetscScalar *vcoords;
2525 
2526     PetscCall(DMLabelGetValue(bodyLabel, v, &bodyID));
2527     PetscCall(DMLabelGetValue(faceLabel, v, &faceID));
2528     PetscCall(DMLabelGetValue(edgeLabel, v, &edgeID));
2529     PetscCall(DMLabelGetValue(vertexLabel, v, &vertexID));
2530 
2531     PetscCheck(bodyID < Nb, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Body %" PetscInt_FMT " is not in [0, %" PetscInt_FMT ")", bodyID, Nb);
2532     body = bodies[bodyID];
2533 
2534     PetscCall(DMPlexPointLocalRef(cdm, v, coords, (void *)&vcoords));
2535     if (vertexID > 0) {
2536       if (islite) {
2537         PetscCall(EGlite_objectBodyTopo(body, NODE, vertexID, &vertex));
2538         PetscCall(EGlite_evaluate(vertex, NULL, result));
2539       } else {
2540         PetscCall(EG_objectBodyTopo(body, NODE, vertexID, &vertex));
2541         PetscCall(EG_evaluate(vertex, NULL, result));
2542       }
2543       for (d = 0; d < cdim; ++d) vcoords[d] = result[d];
2544     } else if (edgeID > 0) {
2545       /* Snap to EDGE at nearest location */
2546       double params[1];
2547       if (islite) {
2548         PetscCall(EGlite_objectBodyTopo(body, EDGE, edgeID, &edge));
2549         PetscCall(EGlite_invEvaluate(edge, vcoords, params, result));
2550       } // Get (x,y,z) of nearest point on EDGE
2551       else {
2552         PetscCall(EG_objectBodyTopo(body, EDGE, edgeID, &edge));
2553         PetscCall(EG_invEvaluate(edge, vcoords, params, result));
2554       }
2555       for (d = 0; d < cdim; ++d) vcoords[d] = result[d];
2556     } else if (faceID > 0) {
2557       /* Snap to FACE at nearest location */
2558       double params[2];
2559       if (islite) {
2560         PetscCall(EGlite_objectBodyTopo(body, FACE, faceID, &face));
2561         PetscCall(EGlite_invEvaluate(face, vcoords, params, result));
2562       } // Get (x,y,z) of nearest point on FACE
2563       else {
2564         PetscCall(EG_objectBodyTopo(body, FACE, faceID, &face));
2565         PetscCall(EG_invEvaluate(face, vcoords, params, result));
2566       }
2567       for (d = 0; d < cdim; ++d) vcoords[d] = result[d];
2568     }
2569   }
2570   PetscCall(VecRestoreArrayWrite(coordinates, &coords));
2571   /* Clear out global coordinates */
2572   PetscCall(VecDestroy(&dm->coordinates[0].x));
2573 #endif
2574   PetscFunctionReturn(PETSC_SUCCESS);
2575 }
2576 
2577 #if defined(PETSC_HAVE_EGADS)
2578 // This replaces the model in-place
2579 PetscErrorCode ConvertGeomModelToAllBSplines(PetscBool islite, ego *model)
2580 {
2581   /* EGADS/EGADSlite Variables */
2582   ego  context = NULL, geom, *bodies, *fobjs;
2583   int  oclass, mtype;
2584   int *senses;
2585   int  Nb, Nf;
2586 
2587   PetscFunctionBegin;
2588   // Get the number of bodies and body objects in the model
2589   if (islite) PetscCallEGADS(EGlite_getTopology, (*model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
2590   else PetscCallEGADS(EG_getTopology, (*model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
2591 
2592   // Get all Faces on the body    <-- Only working with 1 body at the moment.
2593   ego body = bodies[0];
2594   if (islite) PetscCallEGADS(EGlite_getBodyTopos, (body, NULL, FACE, &Nf, &fobjs));
2595   else PetscCallEGADS(EG_getBodyTopos, (body, NULL, FACE, &Nf, &fobjs));
2596   ego newGeom[Nf];
2597   ego newFaces[Nf];
2598 
2599   // Convert the 1st Face to a BSpline Geometry
2600   for (int ii = 0; ii < Nf; ++ii) {
2601     ego     face = fobjs[ii];
2602     ego     gRef, gPrev, gNext, *lobjs;
2603     int     goclass, gmtype, *gpinfo;
2604     int     Nl, *lsenses;
2605     double *gprv;
2606     char   *gClass = (char *)"", *gType = (char *)"";
2607 
2608     /* Shape Optimization is NOT available for EGADSlite geometry files. */
2609     /*     Note :: islite options are left below in case future versions of EGADSlite includes this capability */
2610     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");
2611 
2612     if (islite) {
2613       PetscCallEGADS(EGlite_getTopology, (face, &geom, &oclass, &mtype, NULL, &Nl, &lobjs, &lsenses)); // Get FACES Geometry object (geom_
2614       PetscCallEGADS(EGlite_getGeometry, (geom, &goclass, &gmtype, &gRef, &gpinfo, &gprv));            // Get geometry object info
2615       PetscCallEGADS(EGlite_getInfo, (geom, &goclass, &gmtype, &gRef, &gPrev, &gNext));
2616     } // Get geometry info
2617     else {
2618       PetscCallEGADS(EG_getTopology, (face, &geom, &oclass, &mtype, NULL, &Nl, &lobjs, &lsenses)); // Get FACES Geometry object (geom_
2619       PetscCallEGADS(EG_getGeometry, (geom, &goclass, &gmtype, &gRef, &gpinfo, &gprv));            // Get geometry object info
2620       PetscCallEGADS(EG_getInfo, (geom, &goclass, &gmtype, &gRef, &gPrev, &gNext));
2621     } // Get geometry info
2622 
2623     PetscCall(DMPlex_EGADS_GeomDecode_Internal(goclass, gmtype, &gClass, &gType)); // Decode Geometry integers
2624 
2625     // Convert current FACE to a BSpline Surface
2626     ego     bspline;
2627     ego     bRef, bPrev, bNext;
2628     int     boclass, bmtype, *bpinfo;
2629     double *bprv;
2630     char   *bClass = (char *)"", *bType = (char *)"";
2631 
2632     PetscCallEGADS(EG_convertToBSpline, (face, &bspline)); // Does not have an EGlite_ version
2633 
2634     if (islite) {
2635       PetscCallEGADS(EGlite_getGeometry, (bspline, &boclass, &bmtype, &bRef, &bpinfo, &bprv)); // Get geometry object info
2636       PetscCallEGADS(EGlite_getInfo, (bspline, &boclass, &bmtype, &bRef, &bPrev, &bNext));
2637     } // Get geometry info
2638     else {
2639       PetscCallEGADS(EG_getGeometry, (bspline, &boclass, &bmtype, &bRef, &bpinfo, &bprv)); // Get geometry object info
2640       PetscCallEGADS(EG_getInfo, (bspline, &boclass, &bmtype, &bRef, &bPrev, &bNext));
2641     } // Get geometry info
2642 
2643     PetscCall(DMPlex_EGADS_GeomDecode_Internal(boclass, bmtype, &bClass, &bType)); // Decode Geometry integers
2644 
2645     // Get Context from FACE
2646     context = NULL;
2647     PetscCallEGADS(EG_getContext, (face, &context)); // Does not have an EGlite_ version
2648 
2649     // Silence WARNING Regarding OPENCASCADE 7.5
2650     if (islite) PetscCallEGADS(EGlite_setOutLevel, (context, 0));
2651     else PetscCallEGADS(EG_setOutLevel, (context, 0));
2652 
2653     ego newgeom;
2654     PetscCallEGADS(EG_makeGeometry, (context, SURFACE, BSPLINE, NULL, bpinfo, bprv, &newgeom)); // Does not have an EGlite_ version
2655 
2656     PetscCallEGADS(EG_deleteObject, (bspline));
2657 
2658     // Create new FACE based on new SURFACE geometry
2659     double data[4];
2660     int    periodic;
2661     if (islite) PetscCallEGADS(EGlite_getRange, (newgeom, data, &periodic));
2662     else PetscCallEGADS(EG_getRange, (newgeom, data, &periodic));
2663 
2664     ego newface;
2665     PetscCallEGADS(EG_makeFace, (newgeom, SFORWARD, data, &newface)); // Does not have an EGlite_ version
2666     //PetscCallEGADS(EG_deleteObject, (newgeom));
2667     //PetscCallEGADS(EG_deleteObject, (newface));
2668     newFaces[ii] = newface;
2669     newGeom[ii]  = newgeom;
2670 
2671     // Reinstate WARNING Regarding OPENCASCADE 7.5
2672     if (islite) PetscCallEGADS(EGlite_setOutLevel, (context, 1));
2673     else PetscCallEGADS(EG_setOutLevel, (context, 1));
2674   }
2675 
2676   // Sew New Faces together to get a new model
2677   ego newmodel;
2678   PetscCallEGADS(EG_sewFaces, (Nf, newFaces, 0.0, 0, &newmodel)); // Does not have an EGlite_ version
2679   for (int ii = 0; ii < Nf; ++ii) {
2680     PetscCallEGADS(EG_deleteObject, (newFaces[ii]));
2681     PetscCallEGADS(EG_deleteObject, (newGeom[ii]));
2682   }
2683   PetscCallEGADS(EG_deleteObject, (*model));
2684   *model = newmodel;
2685   PetscFunctionReturn(PETSC_SUCCESS);
2686 }
2687 #endif
2688 
2689 /*@C
2690   DMPlexCreateGeomFromFile - Create a `DMPLEX` mesh from an EGADS, IGES, or STEP file.
2691 
2692   Collective
2693 
2694   Input Parameters:
2695 + comm     - The MPI communicator
2696 . filename - The name of the EGADS, IGES, or STEP file
2697 - islite   - Flag for EGADSlite support
2698 
2699   Output Parameter:
2700 . dm - The `DM` object representing the mesh
2701 
2702   Level: beginner
2703 
2704 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexCreateEGADS()`, `DMPlexCreateEGADSliteFromFile()`
2705 @*/
2706 PetscErrorCode DMPlexCreateGeomFromFile(MPI_Comm comm, const char filename[], DM *dm, PetscBool islite)
2707 {
2708   /* PETSc Variables */
2709   PetscMPIInt rank;
2710   PetscBool   printModel = PETSC_FALSE, tessModel = PETSC_FALSE, newModel = PETSC_FALSE;
2711   PetscBool   shapeOpt = PETSC_FALSE;
2712 
2713 #if defined(PETSC_HAVE_EGADS)
2714   ego context = NULL, model = NULL;
2715 #endif
2716 
2717   PetscFunctionBegin;
2718   PetscAssertPointer(filename, 2);
2719   PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_geom_print_model", &printModel, NULL));
2720   PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_geom_tess_model", &tessModel, NULL));
2721   PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_geom_new_model", &newModel, NULL));
2722   PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_geom_shape_opt", &shapeOpt, NULL));
2723   PetscCallMPI(MPI_Comm_rank(comm, &rank));
2724 #if defined(PETSC_HAVE_EGADS)
2725   if (rank == 0) {
2726     /* EGADSlite files cannot be used for Shape Optimization Work. It lacks the ability to make new geometry. */
2727     /* Must use EGADS, STEP, IGES or BRep files to perform this work.                                         */
2728     if (islite) {
2729       PetscCallEGADS(EGlite_open, (&context));
2730       PetscCallEGADS(EGlite_loadModel, (context, 0, filename, &model));
2731       if (shapeOpt) PetscCall(ConvertGeomModelToAllBSplines(islite, &model));
2732       if (printModel) PetscCall(DMPlexGeomPrintModel_Internal(model, islite));
2733     } else {
2734       PetscCallEGADS(EG_open, (&context));
2735       PetscCallEGADS(EG_loadModel, (context, 0, filename, &model));
2736       if (shapeOpt) PetscCall(ConvertGeomModelToAllBSplines(islite, &model));
2737       if (printModel) PetscCall(DMPlexGeomPrintModel_Internal(model, islite));
2738     }
2739   }
2740   if (tessModel) PetscCall(DMPlexCreateGeom_Tess_Internal(comm, context, model, dm, islite));
2741   else if (newModel) PetscCall(DMPlexCreateGeom_Internal(comm, context, model, dm, islite));
2742   else {
2743     PetscCall(DMPlexCreateGeom(comm, context, model, dm, islite));
2744   }
2745   PetscFunctionReturn(PETSC_SUCCESS);
2746 #else
2747   SETERRQ(comm, PETSC_ERR_SUP, "This method requires EGADS support. Reconfigure using --download-egads");
2748 #endif
2749 }
2750 
2751 #if defined(PETSC_HAVE_EGADS)
2752 /*@C
2753   DMPlex_Surface_Grad - Exposes the Geometry's Control Points and Weights and Calculates the Mesh Topology Boundary Nodes Gradient
2754                         with respect the associated geometry's Control Points and Weights.
2755 
2756                         // ----- Depreciated ---- See DMPlexGeomDataAndGrads ------ //
2757 
2758   Collective
2759 
2760   Input Parameters:
2761 . dm      - The DM object representing the mesh with PetscContainer containing an EGADS geometry model
2762 
2763   Output Parameter:
2764 . 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
2765 
2766   Level: intermediate
2767 
2768 .seealso:
2769 @*/
2770 PetscErrorCode DMPlex_Surface_Grad(DM dm)
2771 {
2772   ego            model, geom, *bodies, *fobjs;
2773   PetscContainer modelObj;
2774   int            oclass, mtype, *senses;
2775   int            Nb, Nf;
2776   PetscHMapI     faceCntrlPtRow_Start = NULL, faceCPWeightsRow_Start = NULL;
2777   PetscHMapI     pointSurfGradRow_Start = NULL;
2778   Mat            pointSurfGrad;
2779   IS             faceLabelValues, edgeLabelValues, vertexLabelValues;
2780   PetscInt       faceLabelSize, edgeLabelSize, vertexLabelSize;
2781   PetscBool      islite = PETSC_FALSE;
2782 
2783   PetscFunctionBegin;
2784   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
2785   if (!modelObj) {
2786     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
2787     islite = PETSC_TRUE;
2788     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");
2789   }
2790 
2791   // Get attached EGADS model (pointer)
2792   PetscCall(PetscContainerGetPointer(modelObj, (void **)&model));
2793 
2794   // Get the bodies in the model
2795   if (islite) {
2796     PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
2797   } else {
2798     PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
2799   }
2800 
2801   ego body = bodies[0]; // Only operate on 1st body. Model should only have 1 body.
2802 
2803   // Get the total number of FACEs in the model
2804   if (islite) {
2805     PetscCall(EGlite_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
2806   } else {
2807     PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
2808   }
2809 
2810   // Get the total number of points and IDs in the DMPlex with a "EGADS Face Label"
2811   // This will provide the total number of DMPlex points on the boundary of the geometry
2812   PetscCall(DMGetLabelIdIS(dm, "EGADS Face ID", &faceLabelValues));
2813   PetscCall(DMGetLabelSize(dm, "EGADS Face ID", &faceLabelSize));
2814 
2815   PetscCall(DMGetLabelIdIS(dm, "EGADS Edge ID", &edgeLabelValues));
2816   PetscCall(DMGetLabelSize(dm, "EGADS Edge ID", &edgeLabelSize));
2817 
2818   PetscCall(DMGetLabelIdIS(dm, "EGADS Vertex ID", &vertexLabelValues));
2819   PetscCall(DMGetLabelSize(dm, "EGADS Vertex ID", &vertexLabelSize));
2820 
2821   const PetscInt *faceIndices, *edgeIndices, *vertexIndices;
2822   PetscCall(ISGetIndices(faceLabelValues, &faceIndices));
2823   PetscCall(ISGetIndices(edgeLabelValues, &edgeIndices));
2824   PetscCall(ISGetIndices(vertexLabelValues, &vertexIndices));
2825 
2826   // Get the points associated with each FACE, EDGE and VERTEX label in the DM
2827   PetscInt totalNumPoints = 0;
2828   for (int ii = 0; ii < faceLabelSize; ++ii) {
2829     // Cycle through FACE labels
2830     PetscInt size;
2831     PetscCall(DMGetStratumSize(dm, "EGADS Face ID", faceIndices[ii], &size));
2832     totalNumPoints += size;
2833   }
2834   PetscCall(ISRestoreIndices(faceLabelValues, &faceIndices));
2835   PetscCall(ISDestroy(&faceLabelValues));
2836 
2837   for (int ii = 0; ii < edgeLabelSize; ++ii) {
2838     // Cycle Through EDGE Labels
2839     PetscInt size;
2840     PetscCall(DMGetStratumSize(dm, "EGADS Edge ID", edgeIndices[ii], &size));
2841     totalNumPoints += size;
2842   }
2843   PetscCall(ISRestoreIndices(edgeLabelValues, &edgeIndices));
2844   PetscCall(ISDestroy(&edgeLabelValues));
2845 
2846   for (int ii = 0; ii < vertexLabelSize; ++ii) {
2847     // Cycle Through VERTEX Labels
2848     PetscInt size;
2849     PetscCall(DMGetStratumSize(dm, "EGADS Vertex ID", vertexIndices[ii], &size));
2850     totalNumPoints += size;
2851   }
2852   PetscCall(ISRestoreIndices(vertexLabelValues, &vertexIndices));
2853   PetscCall(ISDestroy(&vertexLabelValues));
2854 
2855   int     maxNumCPs   = 0;
2856   int     totalNumCPs = 0;
2857   ego     bRef, bPrev, bNext, fgeom, *lobjs;
2858   int     id, boclass, bmtype, *bpinfo;
2859   int     foclass, fmtype, Nl, *lsenses;
2860   double *bprv;
2861   double  fdata[4];
2862 
2863   // Create Hash Tables
2864   PetscInt cntr = 0, wcntr = 0;
2865   PetscCall(PetscHMapICreate(&faceCntrlPtRow_Start));
2866   PetscCall(PetscHMapICreate(&faceCPWeightsRow_Start));
2867 
2868   for (int ii = 0; ii < Nf; ++ii) {
2869     // Need to get the maximum number of Control Points defining the FACEs
2870     ego face = fobjs[ii];
2871     int maxNumCPs_temp;
2872 
2873     if (islite) {
2874       id = EGlite_indexBodyTopo(body, face);
2875       PetscCall(EGlite_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
2876       PetscCall(EGlite_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
2877       PetscCall(EGlite_getInfo(fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
2878     } else {
2879       id = EG_indexBodyTopo(body, face);
2880       PetscCall(EG_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
2881       PetscCall(EG_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
2882       PetscCall(EG_getInfo(fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
2883     }
2884 
2885     maxNumCPs_temp = bpinfo[2] * bpinfo[5];
2886     totalNumCPs += bpinfo[2] * bpinfo[5];
2887 
2888     if (maxNumCPs_temp > maxNumCPs) { maxNumCPs = maxNumCPs_temp; }
2889   }
2890 
2891   PetscInt *cpCoordDataLengthPtr, *wDataLengthPtr;
2892   PetscInt  cpCoordDataLength = 3 * totalNumCPs;
2893   PetscInt  wDataLength       = totalNumCPs;
2894   cpCoordDataLengthPtr        = &cpCoordDataLength;
2895   wDataLengthPtr              = &wDataLength;
2896   PetscScalar *cntrlPtCoords, *cntrlPtWeights;
2897   PetscMalloc1(cpCoordDataLength, &cntrlPtCoords);
2898   PetscMalloc1(wDataLength, &cntrlPtWeights);
2899   for (int ii = 0; ii < Nf; ++ii) {
2900     // Need to Populate Control Point Coordinates and Weight Vectors
2901     ego           face = fobjs[ii];
2902     PetscHashIter hashKeyIter, wHashKeyIter;
2903     PetscBool     hashKeyFound, wHashKeyFound;
2904 
2905     if (islite) {
2906       id = EGlite_indexBodyTopo(body, face);
2907       PetscCall(EGlite_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
2908       PetscCall(EGlite_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
2909       PetscCall(EGlite_getInfo(fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
2910     } else {
2911       id = EG_indexBodyTopo(body, face);
2912       PetscCall(EG_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
2913       PetscCall(EG_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
2914       PetscCall(EG_getInfo(fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
2915     }
2916 
2917     // Store Face ID to 1st Row of Control Point Vector
2918     PetscCall(PetscHMapIFind(faceCntrlPtRow_Start, id, &hashKeyIter, &hashKeyFound));
2919 
2920     if (!hashKeyFound) { PetscCall(PetscHMapISet(faceCntrlPtRow_Start, id, cntr)); }
2921 
2922     int offsetCoord = bpinfo[3] + bpinfo[6];
2923     for (int jj = 0; jj < 3 * bpinfo[2] * bpinfo[5]; ++jj) {
2924       cntrlPtCoords[cntr] = bprv[offsetCoord + jj];
2925       cntr += 1;
2926     }
2927 
2928     // Store Face ID to 1st Row of Control Point Weight Vector
2929     PetscCall(PetscHMapIFind(faceCPWeightsRow_Start, id, &wHashKeyIter, &wHashKeyFound));
2930 
2931     if (!wHashKeyFound) { PetscCall(PetscHMapISet(faceCPWeightsRow_Start, id, wcntr)); }
2932 
2933     int offsetWeight = bpinfo[3] + bpinfo[6] + (3 * bpinfo[2] * bpinfo[5]);
2934     for (int jj = 0; jj < bpinfo[2] * bpinfo[5]; ++jj) {
2935       cntrlPtWeights[wcntr] = bprv[offsetWeight + jj];
2936       wcntr += 1;
2937     }
2938   }
2939 
2940   // Attach Control Point and Weight Data to DM
2941   {
2942     PetscContainer cpOrgObj, cpCoordObj, cpCoordLengthObj;
2943     PetscContainer wOrgObj, wValObj, wDataLengthObj;
2944 
2945     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &cpOrgObj));
2946     PetscCall(PetscContainerSetPointer(cpOrgObj, faceCntrlPtRow_Start));
2947     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Hash Table", (PetscObject)cpOrgObj));
2948     PetscCall(PetscContainerDestroy(&cpOrgObj));
2949 
2950     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &cpCoordObj));
2951     PetscCall(PetscContainerSetPointer(cpCoordObj, cntrlPtCoords));
2952     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Coordinates", (PetscObject)cpCoordObj));
2953     PetscCall(PetscContainerDestroy(&cpCoordObj));
2954 
2955     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &cpCoordLengthObj));
2956     PetscCall(PetscContainerSetPointer(cpCoordLengthObj, cpCoordDataLengthPtr));
2957     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Coordinate Data Length", (PetscObject)cpCoordLengthObj));
2958     PetscCall(PetscContainerDestroy(&cpCoordLengthObj));
2959 
2960     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &wOrgObj));
2961     PetscCall(PetscContainerSetPointer(wOrgObj, faceCPWeightsRow_Start));
2962     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weights Hash Table", (PetscObject)wOrgObj));
2963     PetscCall(PetscContainerDestroy(&wOrgObj));
2964 
2965     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &wValObj));
2966     PetscCall(PetscContainerSetPointer(wValObj, cntrlPtWeights));
2967     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weight Data", (PetscObject)wValObj));
2968     PetscCall(PetscContainerDestroy(&wValObj));
2969 
2970     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &wDataLengthObj));
2971     PetscCall(PetscContainerSetPointer(wDataLengthObj, wDataLengthPtr));
2972     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weight Data Length", (PetscObject)wDataLengthObj));
2973     PetscCall(PetscContainerDestroy(&wDataLengthObj));
2974   }
2975 
2976   // Define Matrix to store  Surface Gradient information dx_i/dCPj_i
2977   PetscInt       gcntr   = 0;
2978   const PetscInt rowSize = 3 * maxNumCPs * totalNumPoints;
2979   const PetscInt colSize = 4 * Nf;
2980 
2981   // Create Point Surface Gradient Matrix
2982   MatCreate(PETSC_COMM_WORLD, &pointSurfGrad);
2983   MatSetSizes(pointSurfGrad, PETSC_DECIDE, PETSC_DECIDE, rowSize, colSize);
2984   MatSetType(pointSurfGrad, MATAIJ);
2985   MatSetUp(pointSurfGrad);
2986 
2987   // Create Hash Table to store Point's stare row in surfaceGrad[][]
2988   PetscCall(PetscHMapICreate(&pointSurfGradRow_Start));
2989 
2990   // Get Coordinates for the DMPlex point
2991   DM           cdm;
2992   PetscInt     dE, Nv;
2993   Vec          coordinatesLocal;
2994   PetscScalar *coords = NULL;
2995   PetscCall(DMGetCoordinateDM(dm, &cdm));
2996   PetscCall(DMGetCoordinateDim(dm, &dE));
2997   PetscCall(DMGetCoordinatesLocal(dm, &coordinatesLocal));
2998 
2999   // CYCLE THROUGH FACEs
3000   for (int ii = 0; ii < Nf; ++ii) {
3001     ego             face = fobjs[ii];
3002     ego            *eobjs, *nobjs;
3003     PetscInt        fid, Ne, Nn;
3004     DMLabel         faceLabel, edgeLabel, nodeLabel;
3005     PetscHMapI      currFaceUniquePoints = NULL;
3006     IS              facePoints, edgePoints, nodePoints;
3007     const PetscInt *fIndices, *eIndices, *nIndices;
3008     PetscInt        fSize, eSize, nSize;
3009     PetscHashIter   fHashKeyIter, eHashKeyIter, nHashKeyIter, pHashKeyIter;
3010     PetscBool       fHashKeyFound, eHashKeyFound, nHashKeyFound, pHashKeyFound;
3011     PetscInt        cfCntr = 0;
3012 
3013     // Get Geometry Object for the Current FACE
3014     if (islite) {
3015       PetscCall(EGlite_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
3016       PetscCall(EGlite_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
3017     } else {
3018       PetscCall(EG_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
3019       PetscCall(EG_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
3020     }
3021 
3022     // Get all EDGE and NODE objects attached to the current FACE
3023     if (islite) {
3024       PetscCall(EGlite_getBodyTopos(body, face, EDGE, &Ne, &eobjs));
3025       PetscCall(EGlite_getBodyTopos(body, face, NODE, &Nn, &nobjs));
3026     } else {
3027       PetscCall(EG_getBodyTopos(body, face, EDGE, &Ne, &eobjs));
3028       PetscCall(EG_getBodyTopos(body, face, NODE, &Nn, &nobjs));
3029     }
3030 
3031     // Get all DMPlex Points that have DMLabel "EGADS Face ID" and store them in a Hash Table for later use
3032     if (islite) {
3033       fid = EGlite_indexBodyTopo(body, face);
3034     } else {
3035       fid = EG_indexBodyTopo(body, face);
3036     }
3037 
3038     PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
3039     PetscCall(DMLabelGetStratumIS(faceLabel, fid, &facePoints));
3040     PetscCall(ISGetIndices(facePoints, &fIndices));
3041     PetscCall(ISGetSize(facePoints, &fSize));
3042 
3043     PetscCall(PetscHMapICreate(&currFaceUniquePoints));
3044 
3045     for (int jj = 0; jj < fSize; ++jj) {
3046       PetscCall(PetscHMapIFind(currFaceUniquePoints, fIndices[jj], &fHashKeyIter, &fHashKeyFound));
3047 
3048       if (!fHashKeyFound) {
3049         PetscCall(PetscHMapISet(currFaceUniquePoints, fIndices[jj], cfCntr));
3050         cfCntr += 1;
3051       }
3052 
3053       PetscCall(PetscHMapIFind(pointSurfGradRow_Start, fIndices[jj], &pHashKeyIter, &pHashKeyFound));
3054 
3055       if (!pHashKeyFound) {
3056         PetscCall(PetscHMapISet(pointSurfGradRow_Start, fIndices[jj], gcntr));
3057         gcntr += 3 * maxNumCPs;
3058       }
3059     }
3060     PetscCall(ISRestoreIndices(facePoints, &fIndices));
3061     PetscCall(ISDestroy(&facePoints));
3062 
3063     // 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.
3064     for (int jj = 0; jj < Ne; ++jj) {
3065       ego       edge = eobjs[jj];
3066       PetscBool containLabelValue;
3067 
3068       if (islite) {
3069         id = EGlite_indexBodyTopo(body, edge);
3070       } else {
3071         id = EG_indexBodyTopo(body, edge);
3072       }
3073 
3074       PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
3075       PetscCall(DMLabelHasValue(edgeLabel, id, &containLabelValue));
3076 
3077       if (containLabelValue) {
3078         PetscCall(DMLabelGetStratumIS(edgeLabel, id, &edgePoints));
3079         PetscCall(ISGetIndices(edgePoints, &eIndices));
3080         PetscCall(ISGetSize(edgePoints, &eSize));
3081 
3082         for (int kk = 0; kk < eSize; ++kk) {
3083           PetscCall(PetscHMapIFind(currFaceUniquePoints, eIndices[kk], &eHashKeyIter, &eHashKeyFound));
3084 
3085           if (!eHashKeyFound) {
3086             PetscCall(PetscHMapISet(currFaceUniquePoints, eIndices[kk], cfCntr));
3087             cfCntr += 1;
3088           }
3089 
3090           PetscCall(PetscHMapIFind(pointSurfGradRow_Start, eIndices[kk], &pHashKeyIter, &pHashKeyFound));
3091 
3092           if (!pHashKeyFound) {
3093             PetscCall(PetscHMapISet(pointSurfGradRow_Start, eIndices[kk], gcntr));
3094             gcntr += 3 * maxNumCPs;
3095           }
3096         }
3097         PetscCall(ISRestoreIndices(edgePoints, &eIndices));
3098         PetscCall(ISDestroy(&edgePoints));
3099       }
3100     }
3101 
3102     // 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.
3103     for (int jj = 0; jj < Nn; ++jj) {
3104       ego node = nobjs[jj];
3105 
3106       if (islite) {
3107         id = EGlite_indexBodyTopo(body, node);
3108       } else {
3109         id = EG_indexBodyTopo(body, node);
3110       }
3111 
3112       PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &nodeLabel));
3113       PetscCall(DMLabelGetStratumIS(nodeLabel, id, &nodePoints));
3114       PetscCall(ISGetIndices(nodePoints, &nIndices));
3115       PetscCall(ISGetSize(nodePoints, &nSize));
3116 
3117       for (int kk = 0; kk < nSize; ++kk) {
3118         PetscCall(PetscHMapIFind(currFaceUniquePoints, nIndices[kk], &nHashKeyIter, &nHashKeyFound));
3119 
3120         if (!nHashKeyFound) {
3121           PetscCall(PetscHMapISet(currFaceUniquePoints, nIndices[kk], cfCntr));
3122           cfCntr += 1;
3123         }
3124 
3125         PetscCall(PetscHMapIFind(pointSurfGradRow_Start, nIndices[kk], &pHashKeyIter, &pHashKeyFound));
3126         if (!pHashKeyFound) {
3127           PetscCall(PetscHMapISet(pointSurfGradRow_Start, nIndices[kk], gcntr));
3128           gcntr += 3 * maxNumCPs;
3129         }
3130       }
3131       PetscCall(ISRestoreIndices(nodePoints, &nIndices));
3132       PetscCall(ISDestroy(&nodePoints));
3133     }
3134 
3135     // Get the Total Number of entries in the Hash Table
3136     PetscInt currFaceUPSize;
3137     PetscCall(PetscHMapIGetSize(currFaceUniquePoints, &currFaceUPSize));
3138 
3139     // Get Keys
3140     PetscInt currFaceUPKeys[currFaceUPSize], off = 0;
3141     PetscCall(PetscHMapIGetKeys(currFaceUniquePoints, &off, currFaceUPKeys));
3142 
3143     // Cycle through all points on the current FACE
3144     for (int jj = 0; jj < currFaceUPSize; ++jj) {
3145       PetscInt currPointID = currFaceUPKeys[jj];
3146       PetscCall(DMPlexVecGetClosure(cdm, NULL, coordinatesLocal, currPointID, &Nv, &coords));
3147 
3148       // Get UV position of FACE
3149       double params[2], range[4], eval[18];
3150       int    peri;
3151 
3152       if (islite) {
3153         PetscCall(EGlite_getRange(face, range, &peri));
3154       } else {
3155         PetscCall(EG_getRange(face, range, &peri));
3156       }
3157 
3158       PetscCall(DMPlex_Geom_FACE_XYZtoUV_Internal(coords, face, range, 0, dE, params, islite));
3159 
3160       if (islite) {
3161         PetscCall(EGlite_evaluate(face, params, eval));
3162       } else {
3163         PetscCall(EG_evaluate(face, params, eval));
3164       }
3165 
3166       // Make a new SURFACE Geometry by changing the location of the Control Points
3167       int    prvSize = bpinfo[3] + bpinfo[6] + (4 * bpinfo[2] * bpinfo[5]);
3168       double nbprv[prvSize];
3169 
3170       // Cycle through each Control Point
3171       double deltaCoord = 1.0E-4;
3172       int    offset     = bpinfo[3] + bpinfo[6];
3173       int    wOffset    = offset + (3 * bpinfo[2] * bpinfo[5]);
3174       for (int ii = 0; ii < bpinfo[2] * bpinfo[5]; ++ii) {
3175         // Cycle through each direction (x, then y, then z)
3176         for (int kk = 0; kk < 4; ++kk) {
3177           // Reinitialize nbprv[] values because we only want to change one value at a time
3178           for (int mm = 0; mm < prvSize; ++mm) { nbprv[mm] = bprv[mm]; }
3179 
3180           if (kk == 0) { //X
3181             nbprv[offset + 0] = bprv[offset + 0] + deltaCoord;
3182             nbprv[offset + 1] = bprv[offset + 1];
3183             nbprv[offset + 2] = bprv[offset + 2];
3184           } else if (kk == 1) { //Y
3185             nbprv[offset + 0] = bprv[offset + 0];
3186             nbprv[offset + 1] = bprv[offset + 1] + deltaCoord;
3187             nbprv[offset + 2] = bprv[offset + 2];
3188           } else if (kk == 2) { //Z
3189             nbprv[offset + 0] = bprv[offset + 0];
3190             nbprv[offset + 1] = bprv[offset + 1];
3191             nbprv[offset + 2] = bprv[offset + 2] + deltaCoord;
3192           } else if (kk == 3) { // Weights
3193             nbprv[wOffset + ii] = bprv[wOffset + ii] + deltaCoord;
3194           } else {
3195             // currently do nothing
3196           }
3197 
3198           // Create New Surface Based on New Control Points or Weights
3199           ego newgeom, context;
3200           if (islite) {
3201             PetscCall(EGlite_open(&context));
3202             PetscCall(EGlite_setOutLevel(context, 0));
3203           } else {
3204             PetscCall(EG_open(&context));
3205             PetscCall(EG_setOutLevel(context, 0));
3206           }
3207 
3208           PetscCall(EG_makeGeometry(context, SURFACE, BSPLINE, NULL, bpinfo, nbprv, &newgeom)); // Does not have an EGlite_ version KNOWN_ISSUE
3209 
3210           if (islite) {
3211             PetscCall(EGlite_setOutLevel(context, 1));
3212           } else {
3213             PetscCall(EG_setOutLevel(context, 1));
3214           }
3215 
3216           // Evaluate new (x, y, z) Point Position based on new Surface Definition
3217           double newCoords[18];
3218           if (islite) {
3219             PetscCall(EGlite_getRange(newgeom, range, &peri));
3220           } else {
3221             PetscCall(EG_getRange(newgeom, range, &peri));
3222           }
3223 
3224           PetscCall(DMPlex_Geom_FACE_XYZtoUV_Internal(coords, newgeom, range, 0, dE, params, islite));
3225 
3226           if (islite) {
3227             PetscCall(EGlite_evaluate(newgeom, params, newCoords));
3228           } else {
3229             PetscCall(EG_evaluate(newgeom, params, newCoords));
3230           }
3231 
3232           // Now Calculate the Surface Gradient for the change in x-component Control Point
3233           PetscScalar dxdCx = (newCoords[0] - coords[0]) / deltaCoord;
3234           PetscScalar dxdCy = (newCoords[1] - coords[1]) / deltaCoord;
3235           PetscScalar dxdCz = (newCoords[2] - coords[2]) / deltaCoord;
3236 
3237           // Store Gradient Information in surfaceGrad[][] Matrix
3238           PetscInt startRow;
3239           PetscCall(PetscHMapIGet(pointSurfGradRow_Start, currPointID, &startRow));
3240 
3241           // Store Results in Petsc Matrix
3242           PetscCall(MatSetValue(pointSurfGrad, startRow + (ii * 3) + 0, ((fid - 1) * 4) + kk, dxdCx, INSERT_VALUES));
3243           PetscCall(MatSetValue(pointSurfGrad, startRow + (ii * 3) + 1, ((fid - 1) * 4) + kk, dxdCy, INSERT_VALUES));
3244           PetscCall(MatSetValue(pointSurfGrad, startRow + (ii * 3) + 2, ((fid - 1) * 4) + kk, dxdCz, INSERT_VALUES));
3245         }
3246         offset += 3;
3247       }
3248       PetscCall(DMPlexVecRestoreClosure(cdm, NULL, coordinatesLocal, currPointID, &Nv, &coords));
3249     }
3250   }
3251 
3252   // Assemble Point Surface Grad Matrix
3253   MatAssemblyBegin(pointSurfGrad, MAT_FINAL_ASSEMBLY);
3254   MatAssemblyEnd(pointSurfGrad, MAT_FINAL_ASSEMBLY);
3255 
3256   // Attach Surface Gradient Hash Table and Matrix to DM
3257   {
3258     PetscContainer surfGradOrgObj, surfGradObj;
3259 
3260     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &surfGradOrgObj));
3261     PetscCall(PetscContainerSetPointer(surfGradOrgObj, pointSurfGradRow_Start));
3262     PetscCall(PetscObjectCompose((PetscObject)dm, "Surface Gradient Hash Table", (PetscObject)surfGradOrgObj));
3263     PetscCall(PetscContainerDestroy(&surfGradOrgObj));
3264 
3265     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &surfGradObj));
3266     PetscCall(PetscContainerSetPointer(surfGradObj, pointSurfGrad));
3267     PetscCall(PetscObjectCompose((PetscObject)dm, "Surface Gradient Matrix", (PetscObject)surfGradObj));
3268     PetscCall(PetscContainerDestroy(&surfGradObj));
3269   }
3270   if (islite) EGlite_free(fobjs);
3271   else EG_free(fobjs);
3272   PetscFunctionReturn(PETSC_SUCCESS);
3273 }
3274 
3275 static PetscErrorCode DestroyHashMap(void **p)
3276 {
3277   PetscFunctionBegin;
3278   PetscCall(PetscHMapIDestroy((PetscHMapI *)p));
3279   PetscFunctionReturn(PETSC_SUCCESS);
3280 }
3281 #endif
3282 
3283 /*@C
3284   DMPlexGeomDataAndGrads - Exposes Control Points and Control Point Weights defining the underlying geometry allowing user manipulation of the geometry.
3285 
3286   Collective
3287 
3288   Input Parameters:
3289 + dm           - The DM object representing the mesh with PetscContainer containing an EGADS geometry model
3290 - fullGeomGrad - PetscBool flag. Determines how the Surface Area and Volume Gradients wrt to Control Points and Control Point Weights are calculated.
3291                       PETSC_FALSE :: Surface Area Gradient wrt Control Points and Control Point Weights are calculated using the change in the local
3292                                      FACE changes (not the entire body). Volume Gradients are not calculated. Faster computations.
3293                       PETSC_TRUE  :: Surface Area Gradietn wrt to Control Points and Control Point Weights are calculated using the change observed in
3294                                      the entire solid body. Volume Gradients are calculated. Slower computation due to the need to generate a new solid
3295                                      body geometry for every Control Point and Control Point Weight change.
3296 
3297   Output Parameter:
3298 . dm - The updated DM object representing the mesh with PetscContainers containing the Control Point, Control Point Weight and Gradient Data.
3299 
3300   Level: intermediate
3301 
3302   Note:
3303   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).
3304 
3305 .seealso: `DMPLEX`, `DMCreate()`, `DMPlexCreateGeom()`, `DMPlexModifyEGADSGeomModel()`
3306 @*/
3307 PetscErrorCode DMPlexGeomDataAndGrads(DM dm, PetscBool fullGeomGrad)
3308 {
3309 #if defined(PETSC_HAVE_EGADS)
3310   /* PETSc Variables */
3311   PetscContainer modelObj;
3312   PetscHMapI     faceCntrlPtRow_Start = NULL, faceCPWeightsRow_Start = NULL;
3313   PetscHMapI     pointSurfGradRow_Start = NULL;
3314   Mat            pointSurfGrad, cpEquiv;
3315   IS             faceLabelValues, edgeLabelValues, vertexLabelValues;
3316   PetscInt       faceLabelSize, edgeLabelSize, vertexLabelSize;
3317   PetscBool      islite = PETSC_FALSE;
3318   /* EGADS Variables */
3319   ego model, geom, *bodies, *fobjs = NULL;
3320   int oclass, mtype, *senses;
3321   int Nb, Nf;
3322 #endif
3323 
3324   PetscFunctionBegin;
3325 #if defined(PETSC_HAVE_EGADS)
3326 
3327   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
3328   if (!modelObj) {
3329     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
3330     PetscCheck(modelObj, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Input DM must have attached EGADS Geometry Model");
3331     islite = PETSC_TRUE;
3332     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");
3333   }
3334 
3335   // Get attached EGADS model (pointer)
3336   PetscCall(PetscContainerGetPointer(modelObj, (void **)&model));
3337 
3338   // Get the bodies in the model
3339   if (islite) {
3340     PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
3341   } else {
3342     PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
3343   }
3344 
3345   ego body = bodies[0]; // Only operate on 1st body. Model should only have 1 body.
3346 
3347   // Get the total number of FACEs in the model
3348   if (islite) {
3349     PetscCall(EGlite_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
3350   } else {
3351     PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
3352   }
3353 
3354   // Get the total number of points and IDs in the DMPlex with a "EGADS Face Label"
3355   // This will provide the total number of DMPlex points on the boundary of the geometry
3356   PetscCall(DMGetLabelIdIS(dm, "EGADS Face ID", &faceLabelValues));
3357   PetscCall(DMGetLabelSize(dm, "EGADS Face ID", &faceLabelSize));
3358 
3359   PetscCall(DMGetLabelIdIS(dm, "EGADS Edge ID", &edgeLabelValues));
3360   PetscCall(DMGetLabelSize(dm, "EGADS Edge ID", &edgeLabelSize));
3361 
3362   PetscCall(DMGetLabelIdIS(dm, "EGADS Vertex ID", &vertexLabelValues));
3363   PetscCall(DMGetLabelSize(dm, "EGADS Vertex ID", &vertexLabelSize));
3364 
3365   const PetscInt *faceIndices, *edgeIndices, *vertexIndices;
3366   PetscCall(ISGetIndices(faceLabelValues, &faceIndices));
3367   PetscCall(ISGetIndices(edgeLabelValues, &edgeIndices));
3368   PetscCall(ISGetIndices(vertexLabelValues, &vertexIndices));
3369 
3370   // Get the points associated with each FACE, EDGE and VERTEX label in the DM
3371   PetscInt totalNumPoints = 0;
3372   for (int f = 0; f < faceLabelSize; ++f) {
3373     // Cycle through FACE labels
3374     PetscInt size;
3375     PetscCall(DMGetStratumSize(dm, "EGADS Face ID", faceIndices[f], &size));
3376     totalNumPoints += size;
3377   }
3378   PetscCall(ISRestoreIndices(faceLabelValues, &faceIndices));
3379   PetscCall(ISDestroy(&faceLabelValues));
3380 
3381   for (int e = 0; e < edgeLabelSize; ++e) {
3382     // Cycle Through EDGE Labels
3383     PetscInt size;
3384     PetscCall(DMGetStratumSize(dm, "EGADS Edge ID", edgeIndices[e], &size));
3385     totalNumPoints += size;
3386   }
3387   PetscCall(ISRestoreIndices(edgeLabelValues, &edgeIndices));
3388   PetscCall(ISDestroy(&edgeLabelValues));
3389 
3390   for (int ii = 0; ii < vertexLabelSize; ++ii) {
3391     // Cycle Through VERTEX Labels
3392     PetscInt size;
3393     PetscCall(DMGetStratumSize(dm, "EGADS Vertex ID", vertexIndices[ii], &size));
3394     totalNumPoints += size;
3395   }
3396   PetscCall(ISRestoreIndices(vertexLabelValues, &vertexIndices));
3397   PetscCall(ISDestroy(&vertexLabelValues));
3398 
3399   int     maxNumCPs   = 0;
3400   int     totalNumCPs = 0;
3401   ego     bRef, bPrev, bNext, fgeom, *lobjs;
3402   int     id, boclass, bmtype, *bpinfo;
3403   int     foclass, fmtype, Nl, *lsenses;
3404   double *bprv;
3405   double  fdata[4];
3406 
3407   // Create Hash Tables
3408   PetscInt cntr = 0, wcntr = 0, vcntr = 0;
3409   PetscCall(PetscHMapICreate(&faceCntrlPtRow_Start));
3410   PetscCall(PetscHMapICreate(&faceCPWeightsRow_Start));
3411 
3412   for (int f = 0; f < Nf; ++f) {
3413     // Need to get the maximum number of Control Points defining the FACEs
3414     ego face = fobjs[f];
3415     int maxNumCPs_temp;
3416 
3417     if (islite) {
3418       id = EGlite_indexBodyTopo(body, face);
3419       PetscCall(EGlite_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
3420       PetscCall(EGlite_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
3421       PetscCall(EGlite_getInfo(fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
3422     } else {
3423       id = EG_indexBodyTopo(body, face);
3424       PetscCall(EG_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
3425       PetscCall(EG_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
3426       PetscCall(EG_getInfo(fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
3427     }
3428     maxNumCPs_temp = bpinfo[2] * bpinfo[5];
3429     totalNumCPs += bpinfo[2] * bpinfo[5];
3430 
3431     if (maxNumCPs_temp > maxNumCPs) { maxNumCPs = maxNumCPs_temp; }
3432   }
3433 
3434   PetscInt *cpCoordDataLengthPtr, *wDataLengthPtr;
3435   PetscInt  cpCoordDataLength = 3 * totalNumCPs;
3436   PetscInt  wDataLength       = totalNumCPs;
3437   cpCoordDataLengthPtr        = &cpCoordDataLength;
3438   wDataLengthPtr              = &wDataLength;
3439 
3440   Vec          cntrlPtCoordsVec, cntrlPtWeightsVec;
3441   PetscScalar *cntrlPtCoords, *cntrlPtWeights;
3442   PetscCall(VecCreateSeq(PETSC_COMM_SELF, cpCoordDataLength, &cntrlPtCoordsVec));
3443   PetscCall(VecCreateSeq(PETSC_COMM_SELF, wDataLength, &cntrlPtWeightsVec));
3444 
3445   // For dSA/dCPi
3446   Vec          gradSACPVec, gradSAWVec, gradVCPVec, gradVWVec;
3447   PetscScalar *gradSACP, *gradSAW, *gradVCP, *gradVW;
3448   PetscCall(VecCreateSeq(PETSC_COMM_SELF, cpCoordDataLength, &gradSACPVec));
3449   PetscCall(VecCreateSeq(PETSC_COMM_SELF, wDataLength, &gradSAWVec));
3450   PetscCall(VecCreateSeq(PETSC_COMM_SELF, cpCoordDataLength, &gradVCPVec));
3451   PetscCall(VecCreateSeq(PETSC_COMM_SELF, wDataLength, &gradVWVec));
3452 
3453   // Control Point - Vertex/Edge/Face Relationship
3454   PetscInt *cp_vertex, *cp_edge, *cp_face;
3455   PetscInt *w_vertex, *w_edge, *w_face;
3456   PetscCall(PetscMalloc1(totalNumCPs, &cp_vertex));
3457   PetscCall(PetscMalloc1(totalNumCPs, &cp_edge));
3458   PetscCall(PetscMalloc1(totalNumCPs, &cp_face));
3459   PetscCall(PetscMalloc1(wDataLength, &w_vertex));
3460   PetscCall(PetscMalloc1(wDataLength, &w_edge));
3461   PetscCall(PetscMalloc1(wDataLength, &w_face));
3462 
3463   for (int f = 0; f < Nf; ++f) {
3464     // Need to Populate Control Point Coordinates and Weight Vectors
3465     ego           face = fobjs[f];
3466     ego          *vobjs, *eobjs;
3467     int           offsetCoord, offsetWeight;
3468     PetscInt      Nv, Ne, wRowStart = 0;
3469     PetscHashIter hashKeyIter, wHashKeyIter;
3470     PetscBool     hashKeyFound, wHashKeyFound;
3471 
3472     if (islite) {
3473       id = EGlite_indexBodyTopo(body, face);
3474       PetscCallEGADS(EGlite_getTopology, (face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
3475       PetscCallEGADS(EGlite_getGeometry, (fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
3476       PetscCallEGADS(EGlite_getInfo, (fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
3477       PetscCallEGADS(EGlite_getBodyTopos, (body, face, NODE, &Nv, &vobjs));
3478     } else {
3479       id = EG_indexBodyTopo(body, face);
3480       PetscCallEGADS(EG_getTopology, (face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
3481       PetscCallEGADS(EG_getGeometry, (fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
3482       PetscCallEGADS(EG_getInfo, (fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
3483       PetscCallEGADS(EG_getBodyTopos, (body, face, NODE, &Nv, &vobjs));
3484     }
3485 
3486     // Store Face ID to 1st Row of Control Point Vector
3487     PetscCall(PetscHMapIFind(faceCntrlPtRow_Start, id, &hashKeyIter, &hashKeyFound));
3488 
3489     if (!hashKeyFound) PetscCall(PetscHMapISet(faceCntrlPtRow_Start, id, cntr));
3490 
3491     PetscCall(VecGetArrayWrite(cntrlPtCoordsVec, &cntrlPtCoords));
3492     offsetCoord = bpinfo[3] + bpinfo[6];
3493     for (int jj = 0; jj < 3 * bpinfo[2] * bpinfo[5]; ++jj) {
3494       cntrlPtCoords[cntr] = bprv[offsetCoord + jj];
3495       cntr += 1;
3496     }
3497 
3498     // Store Face ID to 1st Row of Control Point Weight Vector
3499     PetscCall(PetscHMapIFind(faceCPWeightsRow_Start, id, &wHashKeyIter, &wHashKeyFound));
3500 
3501     if (!wHashKeyFound) {
3502       PetscCall(PetscHMapISet(faceCPWeightsRow_Start, id, wcntr));
3503       wRowStart = wcntr;
3504     }
3505 
3506     PetscCall(VecGetArrayWrite(cntrlPtWeightsVec, &cntrlPtWeights));
3507     offsetWeight = bpinfo[3] + bpinfo[6] + (3 * bpinfo[2] * bpinfo[5]);
3508     for (int jj = 0; jj < bpinfo[2] * bpinfo[5]; ++jj) {
3509       cntrlPtWeights[wcntr] = bprv[offsetWeight + jj];
3510       cp_face[wcntr]        = id;
3511       w_face[wcntr]         = id;
3512       wcntr += 1;
3513     }
3514     PetscCall(VecRestoreArrayWrite(cntrlPtWeightsVec, &cntrlPtWeights));
3515 
3516     // Associate Control Points with Vertix IDs
3517     PetscScalar xcp, ycp, zcp;
3518     offsetCoord = bpinfo[3] + bpinfo[6];
3519     for (int jj = 0; jj < 3 * bpinfo[2] * bpinfo[5]; jj += 3) {
3520       xcp = bprv[offsetCoord + jj + 0];
3521       ycp = bprv[offsetCoord + jj + 1];
3522       zcp = bprv[offsetCoord + jj + 2];
3523 
3524       //Initialize Control Point and Weight to Vertex ID relationship to -1
3525       cp_vertex[vcntr] = -1;
3526       w_vertex[vcntr]  = -1;
3527       cp_edge[vcntr]   = -1;
3528       w_edge[vcntr]    = -1;
3529 
3530       for (int kk = 0; kk < Nv; ++kk) {
3531         int         vid;
3532         double      vCoords[3];
3533         PetscScalar vDelta;
3534         ego         vertex = vobjs[kk];
3535 
3536         if (islite) {
3537           vid = EGlite_indexBodyTopo(body, vertex);
3538           PetscCallEGADS(EGlite_evaluate, (vertex, NULL, vCoords));
3539         } else {
3540           vid = EG_indexBodyTopo(body, vertex);
3541           PetscCallEGADS(EG_evaluate, (vertex, NULL, vCoords));
3542         }
3543         vDelta = PetscSqrtReal(PetscSqr(vCoords[0] - xcp) + PetscSqr(vCoords[1] - ycp) + PetscSqr(vCoords[2] - zcp));
3544 
3545         if (vDelta < 1.0E-15) {
3546           cp_vertex[vcntr] = vid;
3547           w_vertex[vcntr]  = vid;
3548         }
3549       }
3550       vcntr += 1;
3551     }
3552     // These two line could be replaced with DMPlexFreeGeomObject()
3553     if (islite) EGlite_free(vobjs);
3554     else EG_free(vobjs);
3555 
3556     // Associate Control Points with Edge IDs
3557     if (islite) PetscCallEGADS(EGlite_getBodyTopos, (body, face, EDGE, &Ne, &eobjs));
3558     else PetscCallEGADS(EG_getBodyTopos, (body, face, EDGE, &Ne, &eobjs));
3559 
3560     int cpV1, cpV2;
3561     int minID, maxID;
3562 
3563     // Along vmin axis
3564     minID = wRowStart;
3565     maxID = wRowStart + (bpinfo[2] - 1);
3566     cpV1  = cp_vertex[minID];
3567     cpV2  = cp_vertex[maxID];
3568     for (int jj = 0; jj < Ne; ++jj) {
3569       ego edge = eobjs[jj];
3570       ego egeom, *nobjs;
3571       int eoclass, emtype, Nn, *nsenses;
3572       int n1ID, n2ID, eid;
3573 
3574       if (islite) {
3575         eid = EGlite_indexBodyTopo(body, edge);
3576         PetscCallEGADS(EGlite_getTopology, (edge, &egeom, &eoclass, &emtype, NULL, &Nn, &nobjs, &nsenses));
3577       } else {
3578         eid = EG_indexBodyTopo(body, edge);
3579         PetscCallEGADS(EG_getTopology, (edge, &egeom, &eoclass, &emtype, NULL, &Nn, &nobjs, &nsenses));
3580       }
3581 
3582       if (emtype != DEGENERATE) {
3583         // Get IDs for current Edge's End Vertices
3584         if (islite) {
3585           n1ID = EGlite_indexBodyTopo(body, nobjs[0]);
3586           n2ID = EGlite_indexBodyTopo(body, nobjs[1]);
3587         } else {
3588           n1ID = EG_indexBodyTopo(body, nobjs[0]);
3589           n2ID = EG_indexBodyTopo(body, nobjs[1]);
3590         }
3591 
3592         if ((cpV1 == n1ID || cpV1 == n2ID) && (cpV2 == n1ID || cpV2 == n2ID)) {
3593           for (int kk = minID + 1; kk < maxID; ++kk) {
3594             cp_edge[kk] = eid;
3595             w_edge[kk]  = eid;
3596           }
3597         }
3598       }
3599     }
3600 
3601     // Along vmax axis
3602     minID = wRowStart + (bpinfo[2] * (bpinfo[5] - 1));
3603     maxID = wRowStart + (bpinfo[2] * bpinfo[5] - 1);
3604 
3605     cpV1 = cp_vertex[minID];
3606     cpV2 = cp_vertex[maxID];
3607     for (int jj = 0; jj < Ne; ++jj) {
3608       ego edge = eobjs[jj];
3609       ego egeom, *nobjs;
3610       int eoclass, emtype, Nn, *nsenses;
3611       int n1ID, n2ID, eid;
3612 
3613       if (islite) {
3614         eid = EGlite_indexBodyTopo(body, edge);
3615         PetscCallEGADS(EGlite_getTopology, (edge, &egeom, &eoclass, &emtype, NULL, &Nn, &nobjs, &nsenses));
3616       } else {
3617         eid = EG_indexBodyTopo(body, edge);
3618         PetscCallEGADS(EG_getTopology, (edge, &egeom, &eoclass, &emtype, NULL, &Nn, &nobjs, &nsenses));
3619       }
3620 
3621       if (emtype != DEGENERATE) {
3622         // Get IDs for current Edge's End Vertices
3623         if (islite) {
3624           n1ID = EGlite_indexBodyTopo(body, nobjs[0]);
3625           n2ID = EGlite_indexBodyTopo(body, nobjs[1]);
3626         } else {
3627           n1ID = EG_indexBodyTopo(body, nobjs[0]);
3628           n2ID = EG_indexBodyTopo(body, nobjs[1]);
3629         }
3630 
3631         if ((cpV1 == n1ID || cpV1 == n2ID) && (cpV2 == n1ID || cpV2 == n2ID)) {
3632           for (int kk = minID + 1; kk < maxID - 1; ++kk) {
3633             cp_edge[kk] = eid;
3634             w_edge[kk]  = eid;
3635           }
3636         }
3637       }
3638     }
3639 
3640     // Along umin axis
3641     minID = wRowStart;
3642     maxID = wRowStart + (bpinfo[2] * (bpinfo[5] - 1));
3643 
3644     cpV1 = cp_vertex[minID];
3645     cpV2 = cp_vertex[maxID];
3646     for (int jj = 0; jj < Ne; ++jj) {
3647       ego edge = eobjs[jj];
3648       ego egeom, *nobjs;
3649       int eoclass, emtype, Nn, *nsenses;
3650       int n1ID, n2ID, eid;
3651 
3652       if (islite) {
3653         eid = EGlite_indexBodyTopo(body, edge);
3654         PetscCallEGADS(EGlite_getTopology, (edge, &egeom, &eoclass, &emtype, NULL, &Nn, &nobjs, &nsenses));
3655       } else {
3656         eid = EG_indexBodyTopo(body, edge);
3657         PetscCallEGADS(EG_getTopology, (edge, &egeom, &eoclass, &emtype, NULL, &Nn, &nobjs, &nsenses));
3658       }
3659 
3660       if (emtype != DEGENERATE) {
3661         // Get IDs for current Edge's End Vertices
3662         if (islite) {
3663           n1ID = EGlite_indexBodyTopo(body, nobjs[0]);
3664           n2ID = EGlite_indexBodyTopo(body, nobjs[1]);
3665         } else {
3666           n1ID = EG_indexBodyTopo(body, nobjs[0]);
3667           n2ID = EG_indexBodyTopo(body, nobjs[1]);
3668         }
3669 
3670         if ((cpV1 == n1ID || cpV1 == n2ID) && (cpV2 == n1ID || cpV2 == n2ID)) {
3671           for (int kk = minID + bpinfo[2]; kk < maxID; kk += bpinfo[2]) {
3672             cp_edge[kk] = eid;
3673             w_edge[kk]  = eid;
3674           }
3675         }
3676       }
3677     }
3678 
3679     // Along umax axis
3680     minID = wRowStart + (bpinfo[2] - 1);
3681     maxID = wRowStart + (bpinfo[2] * bpinfo[5]) - 1;
3682     cpV1  = cp_vertex[minID];
3683     cpV2  = cp_vertex[maxID];
3684     for (int jj = 0; jj < Ne; ++jj) {
3685       ego edge = eobjs[jj];
3686       ego egeom, *nobjs;
3687       int eoclass, emtype, Nn, *nsenses;
3688       int n1ID, n2ID, eid;
3689 
3690       if (islite) {
3691         eid = EGlite_indexBodyTopo(body, edge);
3692         PetscCallEGADS(EGlite_getTopology, (edge, &egeom, &eoclass, &emtype, NULL, &Nn, &nobjs, &nsenses));
3693       } else {
3694         eid = EG_indexBodyTopo(body, edge);
3695         PetscCallEGADS(EG_getTopology, (edge, &egeom, &eoclass, &emtype, NULL, &Nn, &nobjs, &nsenses));
3696       }
3697 
3698       if (emtype != DEGENERATE) {
3699         // Get IDs for current Edge's End Vertices
3700         if (islite) {
3701           n1ID = EGlite_indexBodyTopo(body, nobjs[0]);
3702           n2ID = EGlite_indexBodyTopo(body, nobjs[1]);
3703         } else {
3704           n1ID = EG_indexBodyTopo(body, nobjs[0]);
3705           n2ID = EG_indexBodyTopo(body, nobjs[1]);
3706         }
3707 
3708         if ((cpV1 == n1ID || cpV1 == n2ID) && (cpV2 == n1ID || cpV2 == n2ID)) {
3709           for (int kk = minID + bpinfo[2]; kk < maxID; kk += bpinfo[2]) {
3710             cp_edge[kk] = eid;
3711             w_edge[kk]  = eid;
3712           }
3713         }
3714       }
3715     }
3716     // These two lines could be replaced with DMPlexFreeGeomObject()
3717     if (islite) EGlite_free(eobjs);
3718     else EG_free(eobjs);
3719   }
3720 
3721   // Determine Control Point Equivalance Matrix relating Control Points between Surfaces
3722   //     Note: The Weights will also be tied together in the same manner
3723   //           Also can use the Weight Hash Table for Row Start ID of each Face
3724   const PetscInt cpRowSize = totalNumCPs;
3725   const PetscInt cpColSize = cpRowSize;
3726   PetscInt      *maxNumRelatePtr;
3727   PetscInt       maxNumRelate = 0;
3728 
3729   // Create Point Surface Gradient Matrix
3730   PetscCall(MatCreate(PETSC_COMM_WORLD, &cpEquiv));
3731   PetscCall(MatSetSizes(cpEquiv, PETSC_DECIDE, PETSC_DECIDE, cpRowSize, cpColSize));
3732   PetscCall(MatSetType(cpEquiv, MATAIJ));
3733   PetscCall(MatSetUp(cpEquiv));
3734 
3735   for (int ii = 0; ii < totalNumCPs; ++ii) {
3736     PetscScalar x1, y1, z1;
3737     PetscInt    maxRelateTemp = 0;
3738 
3739     x1 = cntrlPtCoords[(3 * ii) + 0];
3740     y1 = cntrlPtCoords[(3 * ii) + 1];
3741     z1 = cntrlPtCoords[(3 * ii) + 2];
3742 
3743     for (int jj = 0; jj < totalNumCPs; ++jj) {
3744       PetscScalar x2, y2, z2;
3745       PetscScalar cpDelta, eqFactor;
3746       x2 = cntrlPtCoords[(3 * jj) + 0];
3747       y2 = cntrlPtCoords[(3 * jj) + 1];
3748       z2 = cntrlPtCoords[(3 * jj) + 2];
3749 
3750       cpDelta = PetscSqrtReal(PetscSqr(x2 - x1) + PetscSqr(y2 - y1) + PetscSqr(z2 - z1));
3751       if (cpDelta < 1.0E-15) {
3752         eqFactor = 1.0;
3753         maxRelateTemp += 1;
3754       } else {
3755         eqFactor = 0.0;
3756       }
3757 
3758       // Store Results in Petsc Matrix
3759       PetscCall(MatSetValue(cpEquiv, ii, jj, eqFactor, INSERT_VALUES));
3760     }
3761     if (maxRelateTemp > maxNumRelate) maxNumRelate = maxRelateTemp;
3762   }
3763   maxNumRelatePtr = &maxNumRelate;
3764   PetscCall(VecRestoreArrayWrite(cntrlPtCoordsVec, &cntrlPtCoords));
3765 
3766   // Assemble Point Surface Grad Matrix
3767   PetscCall(MatAssemblyBegin(cpEquiv, MAT_FINAL_ASSEMBLY));
3768   PetscCall(MatAssemblyEnd(cpEquiv, MAT_FINAL_ASSEMBLY));
3769 
3770   // Attach Control Point and Weight Data to DM
3771   {
3772     PetscContainer cpOrgObj, cpCoordLengthObj;
3773     PetscContainer wOrgObj, wDataLengthObj;
3774     PetscContainer cp_faceObj, cp_edgeObj, cp_vertexObj;
3775     PetscContainer w_faceObj, w_edgeObj, w_vertexObj;
3776     PetscContainer maxNumRelateObj;
3777 
3778     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Hash Table", (PetscObject *)&cpOrgObj));
3779     if (!cpOrgObj) {
3780       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &cpOrgObj));
3781       PetscCall(PetscContainerSetPointer(cpOrgObj, faceCntrlPtRow_Start));
3782       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Hash Table", (PetscObject)cpOrgObj));
3783       PetscCall(PetscContainerDestroy(&cpOrgObj));
3784     } else {
3785       PetscCall(PetscContainerSetPointer(cpOrgObj, faceCntrlPtRow_Start));
3786     }
3787 
3788     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Coordinates", (PetscObject)cntrlPtCoordsVec));
3789     PetscCall(VecDestroy(&cntrlPtCoordsVec));
3790 
3791     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Coordinate Data Length", (PetscObject *)&cpCoordLengthObj));
3792     if (!cpCoordLengthObj) {
3793       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &cpCoordLengthObj));
3794       PetscCall(PetscContainerSetPointer(cpCoordLengthObj, cpCoordDataLengthPtr));
3795       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Coordinate Data Length", (PetscObject)cpCoordLengthObj));
3796       PetscCall(PetscContainerDestroy(&cpCoordLengthObj));
3797     } else {
3798       PetscCall(PetscContainerSetPointer(cpCoordLengthObj, cpCoordDataLengthPtr));
3799     }
3800 
3801     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weights Hash Table", (PetscObject *)&wOrgObj));
3802     if (!wOrgObj) {
3803       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &wOrgObj));
3804       PetscCall(PetscContainerSetPointer(wOrgObj, faceCPWeightsRow_Start));
3805       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weights Hash Table", (PetscObject)wOrgObj));
3806       PetscCall(PetscContainerDestroy(&wOrgObj));
3807     } else {
3808       PetscCall(PetscContainerSetPointer(wOrgObj, faceCPWeightsRow_Start));
3809     }
3810 
3811     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weight Data", (PetscObject)cntrlPtWeightsVec));
3812     PetscCall(VecDestroy(&cntrlPtWeightsVec));
3813 
3814     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight Data Length", (PetscObject *)&wDataLengthObj));
3815     if (!wDataLengthObj) {
3816       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &wDataLengthObj));
3817       PetscCall(PetscContainerSetPointer(wDataLengthObj, wDataLengthPtr));
3818       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weight Data Length", (PetscObject)wDataLengthObj));
3819       PetscCall(PetscContainerDestroy(&wDataLengthObj));
3820     } else {
3821       PetscCall(PetscContainerSetPointer(wDataLengthObj, wDataLengthPtr));
3822     }
3823 
3824     PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Equivalancy Matrix", (PetscObject)cpEquiv));
3825 
3826     PetscCall(PetscObjectQuery((PetscObject)dm, "Maximum Number Control Point Equivalency", (PetscObject *)&maxNumRelateObj));
3827     if (!maxNumRelateObj) {
3828       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &maxNumRelateObj));
3829       PetscCall(PetscContainerSetPointer(maxNumRelateObj, maxNumRelatePtr));
3830       PetscCall(PetscObjectCompose((PetscObject)dm, "Maximum Number Control Point Equivalency", (PetscObject)maxNumRelateObj));
3831       PetscCall(PetscContainerDestroy(&maxNumRelateObj));
3832     } else {
3833       PetscCall(PetscContainerSetPointer(maxNumRelateObj, maxNumRelatePtr));
3834     }
3835 
3836     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point - Face Map", (PetscObject *)&cp_faceObj));
3837     if (!cp_faceObj) {
3838       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &cp_faceObj));
3839       PetscCall(PetscContainerSetPointer(cp_faceObj, cp_face));
3840       PetscCall(PetscContainerSetCtxDestroy(cp_faceObj, PetscCtxDestroyDefault));
3841       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point - Face Map", (PetscObject)cp_faceObj));
3842       PetscCall(PetscContainerDestroy(&cp_faceObj));
3843     } else {
3844       void *tmp;
3845 
3846       PetscCall(PetscContainerGetPointer(cp_faceObj, &tmp));
3847       PetscCall(PetscFree(tmp));
3848       PetscCall(PetscContainerSetPointer(cp_faceObj, cp_face));
3849     }
3850 
3851     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight - Face Map", (PetscObject *)&w_faceObj));
3852     if (!w_faceObj) {
3853       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &w_faceObj));
3854       PetscCall(PetscContainerSetPointer(w_faceObj, w_face));
3855       PetscCall(PetscContainerSetCtxDestroy(w_faceObj, PetscCtxDestroyDefault));
3856       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weight - Face Map", (PetscObject)w_faceObj));
3857       PetscCall(PetscContainerDestroy(&w_faceObj));
3858     } else {
3859       void *tmp;
3860 
3861       PetscCall(PetscContainerGetPointer(w_faceObj, &tmp));
3862       PetscCall(PetscFree(tmp));
3863       PetscCall(PetscContainerSetPointer(w_faceObj, w_face));
3864     }
3865 
3866     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point - Edge Map", (PetscObject *)&cp_edgeObj));
3867     if (!cp_edgeObj) {
3868       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &cp_edgeObj));
3869       PetscCall(PetscContainerSetPointer(cp_edgeObj, cp_edge));
3870       PetscCall(PetscContainerSetCtxDestroy(cp_edgeObj, PetscCtxDestroyDefault));
3871       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point - Edge Map", (PetscObject)cp_edgeObj));
3872       PetscCall(PetscContainerDestroy(&cp_edgeObj));
3873     } else {
3874       void *tmp;
3875 
3876       PetscCall(PetscContainerGetPointer(cp_edgeObj, &tmp));
3877       PetscCall(PetscFree(tmp));
3878       PetscCall(PetscContainerSetPointer(cp_edgeObj, cp_edge));
3879     }
3880 
3881     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight - Edge Map", (PetscObject *)&w_edgeObj));
3882     if (!w_edgeObj) {
3883       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &w_edgeObj));
3884       PetscCall(PetscContainerSetPointer(w_edgeObj, w_edge));
3885       PetscCall(PetscContainerSetCtxDestroy(w_edgeObj, PetscCtxDestroyDefault));
3886       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weight - Edge Map", (PetscObject)w_edgeObj));
3887       PetscCall(PetscContainerDestroy(&w_edgeObj));
3888     } else {
3889       void *tmp;
3890 
3891       PetscCall(PetscContainerGetPointer(w_edgeObj, &tmp));
3892       PetscCall(PetscFree(tmp));
3893       PetscCall(PetscContainerSetPointer(w_edgeObj, w_edge));
3894     }
3895 
3896     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point - Vertex Map", (PetscObject *)&cp_vertexObj));
3897     if (!cp_vertexObj) {
3898       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &cp_vertexObj));
3899       PetscCall(PetscContainerSetPointer(cp_vertexObj, cp_vertex));
3900       PetscCall(PetscContainerSetCtxDestroy(cp_vertexObj, PetscCtxDestroyDefault));
3901       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point - Vertex Map", (PetscObject)cp_vertexObj));
3902       PetscCall(PetscContainerDestroy(&cp_vertexObj));
3903     } else {
3904       void *tmp;
3905 
3906       PetscCall(PetscContainerGetPointer(cp_vertexObj, &tmp));
3907       PetscCall(PetscFree(tmp));
3908       PetscCall(PetscContainerSetPointer(cp_vertexObj, cp_vertex));
3909     }
3910 
3911     PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight - Vertex Map", (PetscObject *)&w_vertexObj));
3912     if (!w_vertexObj) {
3913       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &w_vertexObj));
3914       PetscCall(PetscContainerSetPointer(w_vertexObj, w_vertex));
3915       PetscCall(PetscContainerSetCtxDestroy(w_vertexObj, PetscCtxDestroyDefault));
3916       PetscCall(PetscObjectCompose((PetscObject)dm, "Control Point Weight - Vertex Map", (PetscObject)w_vertexObj));
3917       PetscCall(PetscContainerDestroy(&w_vertexObj));
3918     } else {
3919       void *tmp;
3920 
3921       PetscCall(PetscContainerGetPointer(w_vertexObj, &tmp));
3922       PetscCall(PetscFree(tmp));
3923       PetscCall(PetscContainerSetPointer(w_vertexObj, w_vertex));
3924     }
3925   }
3926 
3927   // Define Matrix to store  Geometry Gradient information dGeom_i/dCPj_i
3928   PetscInt       gcntr   = 0;
3929   const PetscInt rowSize = 3 * maxNumCPs * totalNumPoints;
3930   const PetscInt colSize = 4 * Nf;
3931 
3932   // Create Point Surface Gradient Matrix
3933   PetscCall(MatCreate(PETSC_COMM_WORLD, &pointSurfGrad));
3934   PetscCall(MatSetSizes(pointSurfGrad, PETSC_DECIDE, PETSC_DECIDE, rowSize, colSize));
3935   PetscCall(MatSetType(pointSurfGrad, MATAIJ));
3936   PetscCall(MatSetUp(pointSurfGrad));
3937 
3938   // Create Hash Table to store Point's stare row in surfaceGrad[][]
3939   PetscCall(PetscHMapICreate(&pointSurfGradRow_Start));
3940 
3941   // Get Coordinates for the DMPlex point
3942   DM           cdm;
3943   PetscInt     dE, Nv;
3944   Vec          coordinatesLocal;
3945   PetscScalar *coords = NULL;
3946 
3947   PetscCall(DMGetCoordinateDM(dm, &cdm));
3948   PetscCall(DMGetCoordinateDim(dm, &dE));
3949   PetscCall(DMGetCoordinatesLocal(dm, &coordinatesLocal));
3950 
3951   // CYCLE THROUGH FACEs
3952   PetscScalar maxGrad = 0.;
3953   PetscCall(VecGetArrayWrite(gradSACPVec, &gradSACP));
3954   PetscCall(VecGetArrayWrite(gradSAWVec, &gradSAW));
3955   PetscCall(VecGetArrayWrite(gradVCPVec, &gradVCP));
3956   PetscCall(VecGetArrayWrite(gradVWVec, &gradVW));
3957   for (int f = 0; f < Nf; ++f) {
3958     ego             face = fobjs[f];
3959     ego            *eobjs, *nobjs;
3960     PetscInt        fid, Ne, Nn;
3961     DMLabel         faceLabel, edgeLabel, nodeLabel;
3962     PetscHMapI      currFaceUniquePoints = NULL;
3963     IS              facePoints, edgePoints, nodePoints;
3964     const PetscInt *fIndices, *eIndices, *nIndices;
3965     PetscInt        fSize, eSize, nSize;
3966     PetscHashIter   fHashKeyIter, eHashKeyIter, nHashKeyIter, pHashKeyIter;
3967     PetscBool       fHashKeyFound, eHashKeyFound, nHashKeyFound, pHashKeyFound;
3968     PetscInt        cfCntr = 0;
3969 
3970     // Get Geometry Object for the Current FACE
3971     if (islite) {
3972       PetscCall(EGlite_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
3973       PetscCall(EGlite_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
3974     } else {
3975       PetscCall(EG_getTopology(face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
3976       PetscCall(EG_getGeometry(fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
3977     }
3978 
3979     // Get all EDGE and NODE objects attached to the current FACE
3980     if (islite) {
3981       PetscCall(EGlite_getBodyTopos(body, face, EDGE, &Ne, &eobjs));
3982       PetscCall(EGlite_getBodyTopos(body, face, NODE, &Nn, &nobjs));
3983     } else {
3984       PetscCall(EG_getBodyTopos(body, face, EDGE, &Ne, &eobjs));
3985       PetscCall(EG_getBodyTopos(body, face, NODE, &Nn, &nobjs));
3986     }
3987 
3988     // Get all DMPlex Points that have DMLabel "EGADS Face ID" and store them in a Hash Table for later use
3989     if (islite) {
3990       fid = EGlite_indexBodyTopo(body, face);
3991     } else {
3992       fid = EG_indexBodyTopo(body, face);
3993     }
3994 
3995     PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
3996     PetscCall(DMLabelGetStratumIS(faceLabel, fid, &facePoints));
3997     PetscCall(ISGetIndices(facePoints, &fIndices));
3998     PetscCall(ISGetSize(facePoints, &fSize));
3999 
4000     PetscCall(PetscHMapICreate(&currFaceUniquePoints));
4001 
4002     for (int jj = 0; jj < fSize; ++jj) {
4003       PetscCall(PetscHMapIFind(currFaceUniquePoints, fIndices[jj], &fHashKeyIter, &fHashKeyFound));
4004 
4005       if (!fHashKeyFound) {
4006         PetscCall(PetscHMapISet(currFaceUniquePoints, fIndices[jj], cfCntr));
4007         cfCntr += 1;
4008       }
4009 
4010       PetscCall(PetscHMapIFind(pointSurfGradRow_Start, fIndices[jj], &pHashKeyIter, &pHashKeyFound));
4011 
4012       if (!pHashKeyFound) {
4013         PetscCall(PetscHMapISet(pointSurfGradRow_Start, fIndices[jj], gcntr));
4014         gcntr += 3 * maxNumCPs;
4015       }
4016     }
4017     PetscCall(ISRestoreIndices(facePoints, &fIndices));
4018     PetscCall(ISDestroy(&facePoints));
4019 
4020     // 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.
4021     for (int jj = 0; jj < Ne; ++jj) {
4022       ego       edge = eobjs[jj];
4023       PetscBool containLabelValue;
4024 
4025       if (islite) {
4026         id = EGlite_indexBodyTopo(body, edge);
4027       } else {
4028         id = EG_indexBodyTopo(body, edge);
4029       }
4030 
4031       PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
4032       PetscCall(DMLabelHasValue(edgeLabel, id, &containLabelValue));
4033 
4034       if (containLabelValue) {
4035         PetscCall(DMLabelGetStratumIS(edgeLabel, id, &edgePoints));
4036         PetscCall(ISGetIndices(edgePoints, &eIndices));
4037         PetscCall(ISGetSize(edgePoints, &eSize));
4038 
4039         for (int kk = 0; kk < eSize; ++kk) {
4040           PetscCall(PetscHMapIFind(currFaceUniquePoints, eIndices[kk], &eHashKeyIter, &eHashKeyFound));
4041 
4042           if (!eHashKeyFound) {
4043             PetscCall(PetscHMapISet(currFaceUniquePoints, eIndices[kk], cfCntr));
4044             cfCntr += 1;
4045           }
4046 
4047           PetscCall(PetscHMapIFind(pointSurfGradRow_Start, eIndices[kk], &pHashKeyIter, &pHashKeyFound));
4048 
4049           if (!pHashKeyFound) {
4050             PetscCall(PetscHMapISet(pointSurfGradRow_Start, eIndices[kk], gcntr));
4051             gcntr += 3 * maxNumCPs;
4052           }
4053         }
4054         PetscCall(ISRestoreIndices(edgePoints, &eIndices));
4055         PetscCall(ISDestroy(&edgePoints));
4056       }
4057     }
4058 
4059     // 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.
4060     for (int jj = 0; jj < Nn; ++jj) {
4061       ego node = nobjs[jj];
4062 
4063       if (islite) {
4064         id = EGlite_indexBodyTopo(body, node);
4065       } else {
4066         id = EG_indexBodyTopo(body, node);
4067       }
4068 
4069       PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &nodeLabel));
4070       PetscCall(DMLabelGetStratumIS(nodeLabel, id, &nodePoints));
4071       PetscCall(ISGetIndices(nodePoints, &nIndices));
4072       PetscCall(ISGetSize(nodePoints, &nSize));
4073 
4074       for (int kk = 0; kk < nSize; ++kk) {
4075         PetscCall(PetscHMapIFind(currFaceUniquePoints, nIndices[kk], &nHashKeyIter, &nHashKeyFound));
4076 
4077         if (!nHashKeyFound) {
4078           PetscCall(PetscHMapISet(currFaceUniquePoints, nIndices[kk], cfCntr));
4079           cfCntr += 1;
4080         }
4081 
4082         PetscCall(PetscHMapIFind(pointSurfGradRow_Start, nIndices[kk], &pHashKeyIter, &pHashKeyFound));
4083         if (!pHashKeyFound) {
4084           PetscCall(PetscHMapISet(pointSurfGradRow_Start, nIndices[kk], gcntr));
4085           gcntr += 3 * maxNumCPs;
4086         }
4087       }
4088       PetscCall(ISRestoreIndices(nodePoints, &nIndices));
4089       PetscCall(ISDestroy(&nodePoints));
4090     }
4091 
4092     // Get the Total Number of entries in the Hash Table
4093     PetscInt currFaceUPSize;
4094     PetscCall(PetscHMapIGetSize(currFaceUniquePoints, &currFaceUPSize));
4095 
4096     // Get Keys
4097     PetscInt currFaceUPKeys[currFaceUPSize], off = 0;
4098     PetscCall(PetscHMapIGetKeys(currFaceUniquePoints, &off, currFaceUPKeys));
4099     PetscCall(PetscHMapIDestroy(&currFaceUniquePoints));
4100 
4101     // Get Current Face Surface Area
4102     PetscScalar fSA, faceData[14];
4103     PetscCall(EG_getMassProperties(face, faceData)); // This doesn't have a EGlite version. Will it work for EGADSlite files??  KNOWN_ISSUE
4104     fSA = faceData[1];
4105 
4106     // Get Start Row in cpEquiv Matrix
4107     PetscHashIter Witer;
4108     PetscBool     Wfound;
4109     PetscInt      faceWStartRow;
4110     PetscCall(PetscHMapIFind(faceCPWeightsRow_Start, fid, &Witer, &Wfound));
4111     PetscCheck(Wfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "FACE ID not found in Control Point Weights Hash Table");
4112     PetscCall(PetscHMapIGet(faceCPWeightsRow_Start, fid, &faceWStartRow));
4113 
4114     // Cycle through all points on the current FACE
4115     for (int jj = 0; jj < currFaceUPSize; ++jj) {
4116       PetscInt currPointID = currFaceUPKeys[jj];
4117       PetscCall(DMPlexVecGetClosure(cdm, NULL, coordinatesLocal, currPointID, &Nv, &coords));
4118 
4119       // Get UV position of FACE
4120       double params[2], range[4], eval[18];
4121       int    peri;
4122 
4123       if (islite) PetscCall(EGlite_getRange(face, range, &peri));
4124       else PetscCall(EG_getRange(face, range, &peri));
4125 
4126       PetscCall(DMPlex_Geom_FACE_XYZtoUV_Internal(coords, face, range, 0, dE, params, islite));
4127 
4128       if (islite) PetscCall(EGlite_evaluate(face, params, eval));
4129       else PetscCall(EG_evaluate(face, params, eval));
4130 
4131       // Make a new SURFACE Geometry by changing the location of the Control Points
4132       int    prvSize = bpinfo[3] + bpinfo[6] + (4 * bpinfo[2] * bpinfo[5]);
4133       double nbprv[prvSize];
4134 
4135       // Cycle through each Control Point
4136       double denomNew, denomOld;
4137       double deltaCoord = 1.0E-4;
4138       int    offset     = bpinfo[3] + bpinfo[6];
4139       int    wOffset    = offset + (3 * bpinfo[2] * bpinfo[5]);
4140       for (int ii = 0; ii < bpinfo[2] * bpinfo[5]; ++ii) {
4141         PetscCheck(face->blind, PETSC_COMM_SELF, PETSC_ERR_LIB, "Face %d is corrupted: %d %d", f, jj, ii);
4142   #if 0
4143         // Cycle through each direction (x, then y, then z)
4144         if (jj == 0) {
4145           // Get the Number Control Points that are the same as the current points
4146           //    We are looking for repeated Control Points
4147           PetscInt commonCPcntr = 0;
4148           for (int mm = 0; mm < bpinfo[2]*bpinfo[5]; ++mm) {
4149             PetscScalar matValue;
4150             PetscCall(MatGetValue(cpEquiv, faceWStartRow + ii, faceWStartRow + mm, &matValue));
4151 
4152             if (matValue > 0.0) commonCPcntr += 1;
4153           }
4154         }
4155   #endif
4156 
4157         for (int kk = 0; kk < 4; ++kk) {
4158           // Reinitialize nbprv[] values because we only want to change one value at a time
4159           for (int mm = 0; mm < prvSize; ++mm) { nbprv[mm] = bprv[mm]; }
4160           PetscCheck(face->blind, PETSC_COMM_SELF, PETSC_ERR_LIB, "Face %d is corrupted: %d %d %d", f, jj, ii, kk);
4161 
4162           if (kk == 0) { //X
4163             nbprv[offset + 0] = bprv[offset + 0] + deltaCoord;
4164             nbprv[offset + 1] = bprv[offset + 1];
4165             nbprv[offset + 2] = bprv[offset + 2];
4166             denomNew          = nbprv[offset + 0];
4167             denomOld          = bprv[offset + 0];
4168           } else if (kk == 1) { //Y
4169             nbprv[offset + 0] = bprv[offset + 0];
4170             nbprv[offset + 1] = bprv[offset + 1] + deltaCoord;
4171             nbprv[offset + 2] = bprv[offset + 2];
4172             denomNew          = nbprv[offset + 1];
4173             denomOld          = bprv[offset + 1];
4174           } else if (kk == 2) { //Z
4175             nbprv[offset + 0] = bprv[offset + 0];
4176             nbprv[offset + 1] = bprv[offset + 1];
4177             nbprv[offset + 2] = bprv[offset + 2] + deltaCoord;
4178             denomNew          = nbprv[offset + 2];
4179             denomOld          = bprv[offset + 2];
4180           } else if (kk == 3) { // Weights
4181             nbprv[wOffset + ii] = bprv[wOffset + ii] + deltaCoord;
4182             denomNew            = nbprv[wOffset + ii];
4183             denomOld            = bprv[wOffset + ii];
4184           } else {
4185             // currently do nothing
4186           }
4187 
4188           // Create New Surface Based on New Control Points or Weights
4189           ego newgeom, context;
4190           PetscCallEGADS(EG_getContext, (face, &context));                                             // This does not have an EGlite_ version KNOWN_ISSUE
4191           PetscCallEGADS(EG_makeGeometry, (context, SURFACE, BSPLINE, NULL, bpinfo, nbprv, &newgeom)); // This does not have an EGlite_ version KNOWN_ISSUE
4192           PetscCheck(face->blind, PETSC_COMM_SELF, PETSC_ERR_LIB, "Face %d is corrupted: %d %d %d", f, jj, ii, kk);
4193 
4194           // Evaluate new (x, y, z) Point Position based on new Surface Definition
4195           double newCoords[18];
4196           if (islite) PetscCall(EGlite_getRange(newgeom, range, &peri));
4197           else PetscCall(EG_getRange(newgeom, range, &peri));
4198 
4199           PetscCall(DMPlex_Geom_FACE_XYZtoUV_Internal(coords, face, range, 0, dE, params, islite));
4200           PetscCheck(face->blind, PETSC_COMM_SELF, PETSC_ERR_LIB, "Face %d is corrupted: %d %d %d", f, jj, ii, kk);
4201 
4202           if (islite) PetscCall(EGlite_evaluate(newgeom, params, newCoords));
4203           else PetscCall(EG_evaluate(newgeom, params, newCoords));
4204 
4205           // Calculate Surface Area Gradients wrt Control Points and Weights using the local discrete FACE only
4206           //      NOTE 1: Will not provide Volume Gradient wrt to Control Points and Weights.
4207           //      NOTE 2: This is faster than below where an entire new solid geometry is created for each
4208           //              Control Point and Weight gradient
4209           if (!fullGeomGrad) {
4210             // Create new FACE based on new SURFACE geometry
4211             if (jj == 0) { // only for 1st DMPlex Point because we only per CP or Weight
4212               double newFaceRange[4];
4213               int    newFacePeri;
4214               if (islite) PetscCall(EGlite_getRange(newgeom, newFaceRange, &newFacePeri));
4215               else PetscCall(EG_getRange(newgeom, newFaceRange, &newFacePeri));
4216 
4217               ego newface;
4218               PetscCallEGADS(EG_makeFace, (newgeom, SFORWARD, newFaceRange, &newface)); // Does not have EGlite version KNOWN_ISSUE
4219               PetscCheck(face->blind, PETSC_COMM_SELF, PETSC_ERR_LIB, "Face %d is corrupted: %d %d %d", f, jj, ii, kk);
4220 
4221               // Get New Face Surface Area
4222               PetscScalar newfSA, newFaceData[14];
4223               PetscCall(EG_getMassProperties(newface, newFaceData)); // Does not have EGlite version KNOWN_ISSUE
4224               newfSA = newFaceData[1];
4225               PetscCallEGADS(EG_deleteObject, (newface));
4226               PetscCheck(face->blind, PETSC_COMM_SELF, PETSC_ERR_LIB, "Face %d is corrupted: %d %d %d", f, jj, ii, kk);
4227 
4228               // Update Control Points
4229               PetscHashIter CPiter, Witer;
4230               PetscBool     CPfound, Wfound;
4231               PetscInt      faceCPStartRow, faceWStartRow;
4232 
4233               PetscScalar dSAdCPi;
4234               dSAdCPi = (newfSA - fSA) / (denomNew - denomOld);
4235 
4236               if (kk < 3) {
4237                 PetscCall(PetscHMapIFind(faceCntrlPtRow_Start, fid, &CPiter, &CPfound));
4238                 PetscCheck(CPfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "FACE ID not found in Control Point Hash Table");
4239                 PetscCall(PetscHMapIGet(faceCntrlPtRow_Start, fid, &faceCPStartRow));
4240 
4241                 gradSACP[faceCPStartRow + (ii * 3) + kk] = dSAdCPi;
4242 
4243                 if (PetscAbsReal(dSAdCPi) > maxGrad) maxGrad = PetscAbsReal(dSAdCPi);
4244 
4245               } else if (kk == 3) {
4246                 PetscCall(PetscHMapIFind(faceCPWeightsRow_Start, fid, &Witer, &Wfound));
4247                 PetscCheck(Wfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "FACE ID not found in Control Point Hash Table");
4248                 PetscCall(PetscHMapIGet(faceCPWeightsRow_Start, fid, &faceWStartRow));
4249 
4250                 gradSAW[faceWStartRow + ii] = dSAdCPi;
4251 
4252               } else {
4253                 // Do Nothing
4254               }
4255             }
4256           }
4257           PetscCallEGADS(EG_deleteObject, (newgeom));
4258 
4259           // Now Calculate the Surface Gradient for the change in x-component Control Point
4260           PetscScalar dxdCx = (newCoords[0] - coords[0]) / deltaCoord;
4261           PetscScalar dxdCy = (newCoords[1] - coords[1]) / deltaCoord;
4262           PetscScalar dxdCz = (newCoords[2] - coords[2]) / deltaCoord;
4263 
4264           // Store Gradient Information in surfaceGrad[][] Matrix
4265           PetscInt startRow;
4266           PetscCall(PetscHMapIGet(pointSurfGradRow_Start, currPointID, &startRow));
4267 
4268           // Store Results in Petsc Matrix
4269           PetscCall(MatSetValue(pointSurfGrad, startRow + (ii * 3) + 0, ((fid - 1) * 4) + kk, dxdCx, INSERT_VALUES));
4270           PetscCall(MatSetValue(pointSurfGrad, startRow + (ii * 3) + 1, ((fid - 1) * 4) + kk, dxdCy, INSERT_VALUES));
4271           PetscCall(MatSetValue(pointSurfGrad, startRow + (ii * 3) + 2, ((fid - 1) * 4) + kk, dxdCz, INSERT_VALUES));
4272 
4273           //PetscCallEGADS(EG_deleteObject, (newgeom));
4274           PetscCheck(face->blind, PETSC_COMM_SELF, PETSC_ERR_LIB, "Face is corrupted");
4275         }
4276         offset += 3;
4277       }
4278       PetscCall(DMPlexVecRestoreClosure(cdm, NULL, coordinatesLocal, currPointID, &Nv, &coords));
4279     }
4280   }
4281 
4282   // Assemble Point Surface Grad Matrix
4283   PetscCall(MatAssemblyBegin(pointSurfGrad, MAT_FINAL_ASSEMBLY));
4284   PetscCall(MatAssemblyEnd(pointSurfGrad, MAT_FINAL_ASSEMBLY));
4285 
4286   if (fullGeomGrad) {
4287     // Calculate Surface Area and Volume Control Point and Control Point Weight Gradients
4288     //    Note: This is much slower than above due to a new solid geometry being created for
4289     //          each change in Control Point and Control Point Weight. However, this method
4290     //          will provide the Volume Gradient.
4291 
4292     // Get Current Face Surface Area
4293     PetscScalar bodyVol, bodySA, bodyData[14];
4294     PetscCall(EG_getMassProperties(body, bodyData)); // Does not have an EGlite versin KNOWN_ISSUE
4295     bodyVol = bodyData[0];
4296     bodySA  = bodyData[1];
4297 
4298     // Cycle through Control Points
4299     for (int ii = 0; ii < totalNumCPs; ++ii) { // ii should also be the row in cpEquiv for the Control Point
4300       // Cycle through X, Y, Z, W changes
4301       for (int jj = 0; jj < 4; ++jj) {
4302         // Cycle Through Faces
4303         double denomNew = 0.0, denomOld = 0.0;
4304         double deltaCoord = 1.0E-4;
4305         ego    newGeom[Nf];
4306         ego    newFaces[Nf];
4307         for (int kk = 0; kk < Nf; ++kk) {
4308           ego      face;
4309           PetscInt currFID = kk + 1;
4310 
4311           if (islite) {
4312             // Get Current FACE
4313             PetscCallEGADS(EGlite_objectBodyTopo, (body, FACE, currFID, &face));
4314 
4315             // Get Geometry Object for the Current FACE
4316             PetscCallEGADS(EGlite_getTopology, (face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
4317             PetscCallEGADS(EGlite_getGeometry, (fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
4318           } else {
4319             // Get Current FACE
4320             PetscCallEGADS(EG_objectBodyTopo, (body, FACE, currFID, &face));
4321 
4322             // Get Geometry Object for the Current FACE
4323             PetscCallEGADS(EG_getTopology, (face, &fgeom, &foclass, &fmtype, fdata, &Nl, &lobjs, &lsenses));
4324             PetscCallEGADS(EG_getGeometry, (fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
4325           }
4326 
4327           // Make a new SURFACE Geometry by changing the location of the Control Points
4328           int    prvSize = bpinfo[3] + bpinfo[6] + (4 * bpinfo[2] * bpinfo[5]);
4329           double nbprv[prvSize];
4330 
4331           // Reinitialize nbprv[] values because we only want to change one value at a time
4332           for (int mm = 0; mm < prvSize; ++mm) nbprv[mm] = bprv[mm];
4333 
4334           // Get Control Point Row and Column Start for cpEquiv
4335           PetscHashIter Witer;
4336           PetscBool     Wfound;
4337           PetscInt      faceWStartRow;
4338           PetscCall(PetscHMapIFind(faceCPWeightsRow_Start, currFID, &Witer, &Wfound));
4339           PetscCheck(Wfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "FACE ID not found in Control Point Weights Hash Table");
4340           PetscCall(PetscHMapIGet(faceCPWeightsRow_Start, currFID, &faceWStartRow));
4341 
4342           // Modify the Current Control Point on this FACE and All Other FACES
4343           // IMPORTANT!!! If you do not move all identical Control Points on other FACES
4344           //              you will not generate a solid body. You will generate a set of
4345           //              disconnected surfaces that have gap(s) between them.
4346           int offset  = bpinfo[3] + bpinfo[6];
4347           int wOffset = offset + (3 * bpinfo[2] * bpinfo[5]);
4348           for (int mm = 0; mm < bpinfo[2] * bpinfo[5]; ++mm) {
4349             PetscScalar matValue;
4350             PetscCall(MatGetValue(cpEquiv, ii, faceWStartRow + mm, &matValue));
4351 
4352             if (matValue > 0.0) {
4353               if (jj == 0) { //X
4354                 nbprv[offset + (3 * mm) + 0] = bprv[offset + (3 * mm) + 0] + deltaCoord;
4355                 nbprv[offset + (3 * mm) + 1] = bprv[offset + (3 * mm) + 1];
4356                 nbprv[offset + (3 * mm) + 2] = bprv[offset + (3 * mm) + 2];
4357                 denomNew                     = nbprv[offset + (3 * mm) + 0];
4358                 denomOld                     = bprv[offset + (3 * mm) + 0];
4359               } else if (jj == 1) { //Y
4360                 nbprv[offset + (3 * mm) + 0] = bprv[offset + (3 * mm) + 0];
4361                 nbprv[offset + (3 * mm) + 1] = bprv[offset + (3 * mm) + 1] + deltaCoord;
4362                 nbprv[offset + (3 * mm) + 2] = bprv[offset + (3 * mm) + 2];
4363                 denomNew                     = nbprv[offset + (3 * mm) + 1];
4364                 denomOld                     = bprv[offset + (3 * mm) + 1];
4365               } else if (jj == 2) { //Z
4366                 nbprv[offset + (3 * mm) + 0] = bprv[offset + (3 * mm) + 0];
4367                 nbprv[offset + (3 * mm) + 1] = bprv[offset + (3 * mm) + 1];
4368                 nbprv[offset + (3 * mm) + 2] = bprv[offset + (3 * mm) + 2] + deltaCoord;
4369                 denomNew                     = nbprv[offset + (3 * mm) + 2];
4370                 denomOld                     = bprv[offset + (3 * mm) + 2];
4371               } else if (jj == 3) { // Weights
4372                 nbprv[wOffset + mm] = bprv[wOffset + mm] + deltaCoord;
4373                 denomNew            = nbprv[wOffset + mm];
4374                 denomOld            = bprv[wOffset + mm];
4375               } else {
4376                 // currently do nothing
4377               }
4378             }
4379           }
4380 
4381           // Create New Surface Based on New Control Points or Weights
4382           ego newgeom, context;
4383           PetscCallEGADS(EG_getContext, (face, &context));                                             // Does not have an EGlite_ versions   KNOWN_ISSUE
4384           PetscCallEGADS(EG_makeGeometry, (context, SURFACE, BSPLINE, NULL, bpinfo, nbprv, &newgeom)); // Does not have an EGlite_ version KNOWN_ISSUE
4385 
4386           // Create New FACE based on modified geometry
4387           double newFaceRange[4];
4388           int    newFacePeri;
4389           if (islite) PetscCallEGADS(EGlite_getRange, (newgeom, newFaceRange, &newFacePeri));
4390           else PetscCallEGADS(EG_getRange, (newgeom, newFaceRange, &newFacePeri));
4391 
4392           ego newface;
4393           PetscCallEGADS(EG_makeFace, (newgeom, SFORWARD, newFaceRange, &newface)); // Does not have an EGlite_ version KNOWN_ISSUE
4394 
4395           // store new face for later assembly
4396           newGeom[kk]  = newgeom;
4397           newFaces[kk] = newface;
4398         }
4399 
4400         // X-WANT TO BUILD THE NEW GEOMETRY, X-GET NEW SA AND PERFORM dSA/dCPi CALCS HERE <---
4401         // Sew New Faces together to get a new model
4402         ego newmodel;
4403         PetscCall(EG_sewFaces(Nf, newFaces, 0.0, 0, &newmodel)); // Does not have an EGlite_ version KNOWN_ISSUE
4404 
4405         // Get Surface Area and Volume of New/Updated Solid Body
4406         PetscScalar newData[14];
4407         if (islite) PetscCallEGADS(EGlite_getTopology, (newmodel, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
4408         else PetscCallEGADS(EG_getTopology, (newmodel, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
4409 
4410         ego nbody = bodies[0];
4411         PetscCall(EG_getMassProperties(nbody, newData)); // Does not have an EGlite_ version   KNOWN_ISSUE
4412 
4413         PetscScalar dSAdCPi, dVdCPi;
4414         PetscScalar nbodyVol = newData[0], nbodySA = newData[1];
4415 
4416         // Calculate Gradients wrt to Control Points and Control Points Weights depending on jj value
4417         dSAdCPi = (nbodySA - bodySA) / (denomNew - denomOld);
4418         dVdCPi  = (nbodyVol - bodyVol) / (denomNew - denomOld);
4419 
4420         if (jj < 3) {
4421           // Gradienst wrt to Control Points
4422           gradSACP[(ii * 3) + jj] = dSAdCPi;
4423           gradVCP[(ii * 3) + jj]  = dVdCPi;
4424         } else if (jj == 3) {
4425           // Gradients wrt to Control Point Weights
4426           gradSAW[ii] = dSAdCPi;
4427           gradVW[ii]  = dVdCPi;
4428         } else {
4429           // Do Nothing
4430         }
4431         PetscCallEGADS(EG_deleteObject, (newmodel));
4432         for (int kk = 0; kk < Nf; ++kk) {
4433           PetscCallEGADS(EG_deleteObject, (newFaces[kk]));
4434           PetscCallEGADS(EG_deleteObject, (newGeom[kk]));
4435         }
4436       }
4437     }
4438   }
4439   PetscCall(VecRestoreArrayWrite(gradSACPVec, &gradSACP));
4440   PetscCall(VecRestoreArrayWrite(gradSAWVec, &gradSAW));
4441   PetscCall(VecRestoreArrayWrite(gradVCPVec, &gradVCP));
4442   PetscCall(VecRestoreArrayWrite(gradVWVec, &gradVW));
4443   PetscCall(MatDestroy(&cpEquiv));
4444 
4445   // Attach Surface Gradient Hash Table and Matrix to DM
4446   {
4447     PetscContainer surfGradOrgObj;
4448 
4449     PetscCall(PetscObjectQuery((PetscObject)dm, "Surface Gradient Hash Table", (PetscObject *)&surfGradOrgObj));
4450     if (!surfGradOrgObj) {
4451       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &surfGradOrgObj));
4452       PetscCall(PetscContainerSetPointer(surfGradOrgObj, pointSurfGradRow_Start));
4453       PetscCall(PetscContainerSetCtxDestroy(surfGradOrgObj, DestroyHashMap));
4454       PetscCall(PetscObjectCompose((PetscObject)dm, "Surface Gradient Hash Table", (PetscObject)surfGradOrgObj));
4455       PetscCall(PetscContainerDestroy(&surfGradOrgObj));
4456     } else {
4457       PetscCall(PetscContainerSetPointer(surfGradOrgObj, pointSurfGradRow_Start));
4458     }
4459 
4460     PetscCall(PetscObjectCompose((PetscObject)dm, "Surface Gradient Matrix", (PetscObject)pointSurfGrad));
4461     PetscCall(MatDestroy(&pointSurfGrad));
4462 
4463     PetscCall(PetscObjectCompose((PetscObject)dm, "Surface Area Control Point Gradient", (PetscObject)gradSACPVec));
4464     PetscCall(VecDestroy(&gradSACPVec));
4465 
4466     PetscCall(PetscObjectCompose((PetscObject)dm, "Surface Area Weights Gradient", (PetscObject)gradSAWVec));
4467     PetscCall(VecDestroy(&gradSAWVec));
4468 
4469     if (fullGeomGrad) {
4470       PetscCall(PetscObjectCompose((PetscObject)dm, "Volume Control Point Gradient", (PetscObject)gradVCPVec));
4471       PetscCall(PetscObjectCompose((PetscObject)dm, "Volume Weights Gradient", (PetscObject)gradVWVec));
4472     }
4473     PetscCall(VecDestroy(&gradVCPVec));
4474     PetscCall(VecDestroy(&gradVWVec));
4475   }
4476 
4477   // Could be replaced with DMPlexFreeGeomObject()
4478   if (islite) EGlite_free(fobjs);
4479   else EG_free(fobjs);
4480   PetscFunctionReturn(PETSC_SUCCESS);
4481 #else
4482   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "This method requires EGADS support. Reconfigure using --download-egads");
4483 #endif
4484 }
4485 
4486 /*@C
4487   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.
4488 
4489   Collective
4490 
4491   Input Parameters:
4492 + dm          - The DM object representing the mesh with PetscContainer containing an EGADS geometry model
4493 . comm        - MPI_Comm object
4494 . newCP       - C Array of [x, y, z] New/Updated Control Point Coordinates defining the geometry (See DMPlexGeomDataAndGrads() for format)
4495 . newW        - C Array of New/Updated Control Point Weights associated with the Control Points defining the new geometry (See DMPlexGemGrads() for format)
4496 . autoInflate - PetscBool Flag denoting if the user would like to inflate the DM points to the new geometry.
4497 . saveGeom    - PetscBool Flag denoting if the user would iike to save the new geometry to a file.
4498 - 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.
4499                       *.stp or *.step = STEP File
4500                       *.igs or *.iges = IGES File
4501                               *.egads = EGADS File
4502                                *.brep = BRep File (OpenCASCADE File)
4503 
4504   Output Parameter:
4505 . dm - The updated DM object representing the mesh with PetscContainers containing the updated/modified geometry
4506 
4507   Level: intermediate
4508 
4509   Note:
4510   Functionality not available for DMPlexes with attached EGADSlite geometry files (.egadslite).
4511 
4512 .seealso: `DMPLEX`, `DMCreate()`, `DMPlexCreateGeom()`, `DMPlexGeomDataAndGrads()`
4513 @*/
4514 PetscErrorCode DMPlexModifyGeomModel(DM dm, MPI_Comm comm, PetscScalar newCP[], PetscScalar newW[], PetscBool autoInflate, PetscBool saveGeom, const char *stpName)
4515 {
4516 #if defined(PETSC_HAVE_EGADS)
4517   /* EGADS/EGADSlite variables */
4518   ego context, model, geom, *bodies, *lobjs, *fobjs;
4519   int oclass, mtype, *senses, *lsenses;
4520   int Nb, Nf, Nl, id;
4521   /* PETSc variables */
4522   DMLabel        bodyLabel, faceLabel, edgeLabel, vertexLabel;
4523   PetscContainer modelObj, cpHashTableObj, wHashTableObj;
4524   PetscHMapI     cpHashTable = NULL, wHashTable = NULL;
4525   PetscBool      islite = PETSC_FALSE;
4526 #endif
4527 
4528 #if defined(PETSC_HAVE_EGADS)
4529   PetscFunctionBegin;
4530   // Look to see if DM has a Container with either a EGADS or EGADSlite Model
4531   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
4532   if (!modelObj) {
4533     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
4534     islite = PETSC_TRUE;
4535   }
4536   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");
4537   PetscCheck(modelObj, PETSC_COMM_SELF, PETSC_ERR_SUP, "DM does not have a EGADS Geometry Model attached to it!");
4538 
4539   // Get attached EGADS model (pointer)
4540   PetscCall(PetscContainerGetPointer(modelObj, (void **)&model));
4541 
4542   // Look to see if DM has Container for Geometry Control Point Data
4543   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Hash Table", (PetscObject *)&cpHashTableObj));
4544   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weights Hash Table", (PetscObject *)&wHashTableObj));
4545 
4546   PetscCheck(cpHashTableObj && wHashTableObj, PETSC_COMM_SELF, PETSC_ERR_SUP, "DM does not have required Geometry Data attached! Please run DMPlexGeomDataAndGrads() Function first.");
4547 
4548   // Get attached EGADS model Control Point and Weights Hash Tables and Data Arrays (pointer)
4549   PetscCall(PetscContainerGetPointer(cpHashTableObj, (void **)&cpHashTable));
4550   PetscCall(PetscContainerGetPointer(wHashTableObj, (void **)&wHashTable));
4551 
4552   // Get the number of bodies and body objects in the model
4553   if (islite) PetscCallEGADS(EGlite_getTopology, (model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
4554   else PetscCallEGADS(EG_getTopology, (model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
4555 
4556   // Get all Faces on the body
4557   ego body = bodies[0];
4558   if (islite) PetscCallEGADS(EGlite_getBodyTopos, (body, NULL, FACE, &Nf, &fobjs));
4559   else PetscCallEGADS(EG_getBodyTopos, (body, NULL, FACE, &Nf, &fobjs));
4560 
4561   ego newGeom[Nf];
4562   ego newFaces[Nf];
4563 
4564   // Update Control Point and Weight definitions for each surface
4565   for (int jj = 0; jj < Nf; ++jj) {
4566     ego     face = fobjs[jj];
4567     ego     bRef, bPrev, bNext;
4568     ego     fgeom;
4569     int     offset;
4570     int     boclass, bmtype, *bpinfo;
4571     double *bprv;
4572 
4573     // Get FACE ID and other Geometry Data
4574     if (islite) {
4575       id = EGlite_indexBodyTopo(body, face);
4576       PetscCallEGADS(EGlite_getTopology, (face, &fgeom, &oclass, &mtype, NULL, &Nl, &lobjs, &lsenses));
4577       PetscCallEGADS(EGlite_getGeometry, (fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
4578       PetscCallEGADS(EGlite_getInfo, (fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
4579     } else {
4580       id = EG_indexBodyTopo(body, face);
4581       PetscCallEGADS(EG_getTopology, (face, &fgeom, &oclass, &mtype, NULL, &Nl, &lobjs, &lsenses));
4582       PetscCallEGADS(EG_getGeometry, (fgeom, &boclass, &bmtype, &bRef, &bpinfo, &bprv));
4583       PetscCallEGADS(EG_getInfo, (fgeom, &boclass, &bmtype, &bRef, &bPrev, &bNext));
4584     }
4585 
4586     // Update Control Points
4587     PetscHashIter CPiter, Witer;
4588     PetscBool     CPfound, Wfound;
4589     PetscInt      faceCPStartRow, faceWStartRow;
4590 
4591     PetscCall(PetscHMapIFind(cpHashTable, id, &CPiter, &CPfound));
4592     PetscCheck(CPfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "FACE ID not found in Control Point Hash Table");
4593     PetscCall(PetscHMapIGet(cpHashTable, id, &faceCPStartRow));
4594 
4595     PetscCall(PetscHMapIFind(wHashTable, id, &Witer, &Wfound));
4596     PetscCheck(Wfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "FACE ID not found in Control Point Weights Hash Table");
4597     PetscCall(PetscHMapIGet(wHashTable, id, &faceWStartRow));
4598 
4599     // UPDATE CONTROL POINTS Locations
4600     offset = bpinfo[3] + bpinfo[6];
4601     for (int ii = 0; ii < 3 * bpinfo[2] * bpinfo[5]; ++ii) { bprv[offset + ii] = newCP[faceCPStartRow + ii]; }
4602 
4603     // UPDATE CONTROL POINT WEIGHTS
4604     offset = bpinfo[3] + bpinfo[6] + 3 * bpinfo[2] * bpinfo[5];
4605     for (int ii = 0; ii < bpinfo[2] * bpinfo[5]; ++ii) { bprv[offset + ii] = newW[faceWStartRow + ii]; }
4606 
4607     // Get Context from FACE
4608     context = NULL;
4609     PetscCallEGADS(EG_getContext, (face, &context)); // Does not have an EGlite_ version  KNOWN_ISSUE
4610 
4611     // Create New Surface
4612     ego newgeom;
4613     PetscCallEGADS(EG_makeGeometry, (context, SURFACE, BSPLINE, NULL, bpinfo, bprv, &newgeom)); // Does not have an EGlite_ version KNOWN_ISSUE
4614 
4615     // Create new FACE based on new SURFACE geometry
4616     double data[4];
4617     int    periodic;
4618     if (islite) PetscCallEGADS(EGlite_getRange, (newgeom, data, &periodic));
4619     else PetscCallEGADS(EG_getRange, (newgeom, data, &periodic));
4620 
4621     ego newface;
4622     PetscCallEGADS(EG_makeFace, (newgeom, SFORWARD, data, &newface)); // Does not have an EGlite_ version KNOWN_ISSUE
4623     newGeom[jj]  = newgeom;
4624     newFaces[jj] = newface;
4625   }
4626   // Could be replaced by DMPlexFreeGeomObject
4627   if (islite) EGlite_free(fobjs);
4628   else EG_free(fobjs);
4629 
4630   // Sew New Faces together to get a new model
4631   ego newmodel;
4632   PetscCall(EG_sewFaces(Nf, newFaces, 0.0, 0, &newmodel)); // Does not have an EGlite_ version   KNOWN_ISSUE
4633   for (PetscInt f = 0; f < Nf; ++f) {
4634     PetscCallEGADS(EG_deleteObject, (newFaces[f]));
4635     PetscCallEGADS(EG_deleteObject, (newGeom[f]));
4636   }
4637 
4638   // Get the total number of NODEs on the original geometry. (This will be the same for the new geometry)
4639   int  totalNumNode;
4640   ego *nobjTotal;
4641   if (islite) {
4642     PetscCallEGADS(EGlite_getBodyTopos, (body, NULL, NODE, &totalNumNode, &nobjTotal));
4643     EGlite_free(nobjTotal);
4644   } else {
4645     PetscCallEGADS(EG_getBodyTopos, (body, NULL, NODE, &totalNumNode, &nobjTotal));
4646     EG_free(nobjTotal);
4647   } // Could be replaced with DMPlexFreeGeomObject
4648 
4649   // Initialize vector to store equivalent NODE indices between the 2 geometries
4650   // FORMAT :: vector index is the Original Geometry's NODE ID, the vector Value is the New Geometry's NODE ID
4651   int nodeIDEquiv[totalNumNode + 1];
4652 
4653   // Now we need to Map the NODE and EDGE IDs from each Model
4654   if (islite) PetscCallEGADS(EGlite_getBodyTopos, (body, NULL, FACE, &Nf, &fobjs));
4655   else PetscCallEGADS(EG_getBodyTopos, (body, NULL, FACE, &Nf, &fobjs));
4656 
4657   // New CAD
4658   ego *newbodies, newgeomtest, *nfobjs;
4659   int  nNf, newNb, newoclass, newmtype, *newsenses;
4660   if (islite) PetscCallEGADS(EGlite_getTopology, (newmodel, &newgeomtest, &newoclass, &newmtype, NULL, &newNb, &newbodies, &newsenses));
4661   else PetscCallEGADS(EG_getTopology, (newmodel, &newgeomtest, &newoclass, &newmtype, NULL, &newNb, &newbodies, &newsenses));
4662 
4663   ego newbody = newbodies[0];
4664   if (islite) PetscCallEGADS(EGlite_getBodyTopos, (newbody, NULL, FACE, &nNf, &nfobjs));
4665   else PetscCallEGADS(EG_getBodyTopos, (newbody, NULL, FACE, &nNf, &nfobjs));
4666 
4667   PetscCheck(newNb == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "ERROR :: newNb > 1 || newNb = %d", newNb);
4668 
4669   // Find Equivalent Nodes
4670   for (int ii = 0; ii < Nf; ++ii) {
4671     double fdata[4];
4672     int    peri;
4673 
4674     // Get Current FACE [u, v] Ranges
4675     if (islite) PetscCallEGADS(EGlite_getRange, (fobjs[ii], fdata, &peri));
4676     else PetscCallEGADS(EG_getRange, (fobjs[ii], fdata, &peri));
4677 
4678     // Equate NODE IDs between 2 FACEs by working through (u, v) limits of FACE
4679     for (int jj = 0; jj < 2; ++jj) {
4680       for (int kk = 2; kk < 4; ++kk) {
4681         double params[2] = {fdata[jj], fdata[kk]};
4682         double eval[18];
4683         if (islite) PetscCallEGADS(EGlite_evaluate, (fobjs[ii], params, eval));
4684         else PetscCallEGADS(EG_evaluate, (fobjs[ii], params, eval));
4685 
4686         // Original Body
4687         ego *nobjsOrigFace;
4688         int  origNn;
4689         if (islite) PetscCallEGADS(EGlite_getBodyTopos, (body, fobjs[ii], NODE, &origNn, &nobjsOrigFace));
4690         else PetscCallEGADS(EG_getBodyTopos, (body, fobjs[ii], NODE, &origNn, &nobjsOrigFace));
4691 
4692         double minVal = 1.0E10;
4693         double evalCheck[18];
4694         int    equivOrigNodeID = -1;
4695         for (int mm = 0; mm < origNn; ++mm) {
4696           double delta = 1.0E10;
4697           if (islite) PetscCallEGADS(EGlite_evaluate, (nobjsOrigFace[mm], NULL, evalCheck));
4698           else PetscCallEGADS(EG_evaluate, (nobjsOrigFace[mm], NULL, evalCheck));
4699 
4700           delta = PetscSqrtReal(PetscSqr(evalCheck[0] - eval[0]) + PetscSqr(evalCheck[1] - eval[1]) + PetscSqr(evalCheck[2] - eval[2]));
4701 
4702           if (delta < minVal) {
4703             if (islite) equivOrigNodeID = EGlite_indexBodyTopo(body, nobjsOrigFace[mm]);
4704             else equivOrigNodeID = EG_indexBodyTopo(body, nobjsOrigFace[mm]);
4705 
4706             minVal = delta;
4707           }
4708         }
4709         // Could be replaced with DMPlexFreeGeomObject
4710         if (islite) EGlite_free(nobjsOrigFace);
4711         else EG_free(nobjsOrigFace);
4712 
4713         // New Body
4714         ego *nobjsNewFace;
4715         int  newNn;
4716         if (islite) PetscCallEGADS(EGlite_getBodyTopos, (newbody, nfobjs[ii], NODE, &newNn, &nobjsNewFace));
4717         else PetscCallEGADS(EG_getBodyTopos, (newbody, nfobjs[ii], NODE, &newNn, &nobjsNewFace));
4718 
4719         minVal             = 1.0E10;
4720         int equivNewNodeID = -1;
4721         for (int mm = 0; mm < newNn; ++mm) {
4722           double delta = 1.0E10;
4723           if (islite) PetscCallEGADS(EGlite_evaluate, (nobjsNewFace[mm], NULL, evalCheck));
4724           else PetscCallEGADS(EG_evaluate, (nobjsNewFace[mm], NULL, evalCheck));
4725 
4726           delta = PetscSqrtReal(PetscSqr(evalCheck[0] - eval[0]) + PetscSqr(evalCheck[1] - eval[1]) + PetscSqr(evalCheck[2] - eval[2]));
4727 
4728           if (delta < minVal) {
4729             if (islite) equivNewNodeID = EGlite_indexBodyTopo(newbody, nobjsNewFace[mm]);
4730             else equivNewNodeID = EG_indexBodyTopo(newbody, nobjsNewFace[mm]);
4731 
4732             minVal = delta;
4733           }
4734         }
4735         if (islite) EGlite_free(nobjsNewFace);
4736         else EG_free(nobjsNewFace);
4737 
4738         // Store equivalent NODE IDs
4739         nodeIDEquiv[equivOrigNodeID] = equivNewNodeID;
4740       }
4741     }
4742   }
4743 
4744   // Find Equivalent EDGEs
4745   //   Get total number of EDGEs on Original Geometry
4746   int  totalNumEdge;
4747   ego *eobjsOrig;
4748   if (islite) {
4749     PetscCallEGADS(EGlite_getBodyTopos, (body, NULL, EDGE, &totalNumEdge, &eobjsOrig));
4750     EGlite_free(eobjsOrig);
4751   } else {
4752     PetscCallEGADS(EG_getBodyTopos, (body, NULL, EDGE, &totalNumEdge, &eobjsOrig));
4753     EG_free(eobjsOrig);
4754   }
4755 
4756   //   Get total number of EDGEs on New Geometry
4757   int  totalNumEdgeNew;
4758   ego *eobjsNew;
4759   if (islite) {
4760     PetscCallEGADS(EGlite_getBodyTopos, (newbody, NULL, EDGE, &totalNumEdgeNew, &eobjsNew));
4761     EGlite_free(eobjsNew);
4762   } else {
4763     PetscCallEGADS(EG_getBodyTopos, (newbody, NULL, EDGE, &totalNumEdgeNew, &eobjsNew));
4764     EG_free(eobjsNew);
4765   }
4766 
4767   // Initialize EDGE ID equivalent vector
4768   // FORMAT :: vector index is the Original Geometry's EDGE ID, the vector Value is the New Geometry's EDGE ID
4769   int edgeIDEquiv[totalNumEdge + 1];
4770 
4771   // Find Equivalent EDGEs
4772   for (int ii = 0; ii < Nf; ++ii) {
4773     // Get Original Geometry EDGE's NODEs
4774     int numOrigEdge, numNewEdge;
4775     if (islite) {
4776       PetscCallEGADS(EGlite_getBodyTopos, (body, fobjs[ii], EDGE, &numOrigEdge, &eobjsOrig));
4777       PetscCallEGADS(EGlite_getBodyTopos, (newbody, nfobjs[ii], EDGE, &numNewEdge, &eobjsNew));
4778     } else {
4779       PetscCallEGADS(EG_getBodyTopos, (body, fobjs[ii], EDGE, &numOrigEdge, &eobjsOrig));
4780       PetscCallEGADS(EG_getBodyTopos, (newbody, nfobjs[ii], EDGE, &numNewEdge, &eobjsNew));
4781     }
4782 
4783     // new loop below
4784     for (int nn = 0; nn < numOrigEdge; ++nn) {
4785       ego origEdge = eobjsOrig[nn];
4786       ego geomEdgeOrig, *nobjsOrig;
4787       int oclassEdgeOrig, mtypeEdgeOrig;
4788       int NnOrig, *nsensesEdgeOrig;
4789 
4790       if (islite) PetscCallEGADS(EGlite_getTopology, (origEdge, &geomEdgeOrig, &oclassEdgeOrig, &mtypeEdgeOrig, NULL, &NnOrig, &nobjsOrig, &nsensesEdgeOrig));
4791       else PetscCallEGADS(EG_getTopology, (origEdge, &geomEdgeOrig, &oclassEdgeOrig, &mtypeEdgeOrig, NULL, &NnOrig, &nobjsOrig, &nsensesEdgeOrig));
4792 
4793       PetscBool isSame = PETSC_FALSE;
4794       for (int jj = 0; jj < numNewEdge; ++jj) {
4795         ego newEdge = eobjsNew[jj];
4796         ego geomEdgeNew, *nobjsNew;
4797         int oclassEdgeNew, mtypeEdgeNew;
4798         int NnNew, *nsensesEdgeNew;
4799 
4800         if (islite) PetscCallEGADS(EGlite_getTopology, (newEdge, &geomEdgeNew, &oclassEdgeNew, &mtypeEdgeNew, NULL, &NnNew, &nobjsNew, &nsensesEdgeNew));
4801         else PetscCallEGADS(EG_getTopology, (newEdge, &geomEdgeNew, &oclassEdgeNew, &mtypeEdgeNew, NULL, &NnNew, &nobjsNew, &nsensesEdgeNew));
4802 
4803         if (mtypeEdgeOrig == mtypeEdgeNew) {
4804           // Only operate if the EDGE types are the same
4805           for (int kk = 0; kk < NnNew; ++kk) {
4806             int nodeIDOrigGeom, nodeIDNewGeom;
4807             if (islite) {
4808               nodeIDOrigGeom = EGlite_indexBodyTopo(body, nobjsOrig[kk]);
4809               nodeIDNewGeom  = EGlite_indexBodyTopo(newbody, nobjsNew[kk]);
4810             } else {
4811               nodeIDOrigGeom = EG_indexBodyTopo(body, nobjsOrig[kk]);
4812               nodeIDNewGeom  = EG_indexBodyTopo(newbody, nobjsNew[kk]);
4813             }
4814 
4815             if (nodeIDNewGeom == nodeIDEquiv[nodeIDOrigGeom]) {
4816               isSame = PETSC_TRUE;
4817             } else {
4818               isSame = PETSC_FALSE;
4819               kk     = NnNew; // skip ahead because first NODE failed test and order is important
4820             }
4821           }
4822 
4823           if (isSame == PETSC_TRUE) {
4824             int edgeIDOrig, edgeIDNew;
4825             if (islite) {
4826               edgeIDOrig = EGlite_indexBodyTopo(body, origEdge);
4827               edgeIDNew  = EGlite_indexBodyTopo(newbody, newEdge);
4828             } else {
4829               edgeIDOrig = EG_indexBodyTopo(body, origEdge);
4830               edgeIDNew  = EG_indexBodyTopo(newbody, newEdge);
4831             }
4832 
4833             edgeIDEquiv[edgeIDOrig] = edgeIDNew;
4834             jj                      = numNewEdge;
4835           }
4836         }
4837       }
4838     }
4839     if (islite) {
4840       EGlite_free(eobjsOrig);
4841       EGlite_free(eobjsNew);
4842     } else {
4843       EG_free(eobjsOrig);
4844       EG_free(eobjsNew);
4845     }
4846   }
4847   if (islite) {
4848     EGlite_free(fobjs);
4849     EGlite_free(nfobjs);
4850   } else {
4851     EG_free(fobjs);
4852     EG_free(nfobjs);
4853   }
4854 
4855   // Modify labels to point to the IDs on the new Geometry
4856   IS isNodeID, isEdgeID;
4857 
4858   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
4859   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
4860   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
4861   PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));
4862 
4863   PetscCall(ISCreateGeneral(comm, totalNumNode + 1, nodeIDEquiv, PETSC_COPY_VALUES, &isNodeID));
4864   PetscCall(ISCreateGeneral(comm, totalNumEdge + 1, edgeIDEquiv, PETSC_COPY_VALUES, &isEdgeID));
4865   /* Do not perform check. Np may != Nv due to Degenerate Geometry which is not stored in labels.               */
4866   /* We do not know in advance which IDs have been omitted. This may also change due to geometry modifications. */
4867   PetscCall(DMLabelRewriteValues(vertexLabel, isNodeID));
4868   PetscCall(DMLabelRewriteValues(edgeLabel, isEdgeID));
4869   PetscCall(ISDestroy(&isNodeID));
4870   PetscCall(ISDestroy(&isEdgeID));
4871 
4872   // Attempt to point to the new geometry
4873   PetscCallEGADS(EG_deleteObject, (model));
4874   PetscCall(PetscContainerSetPointer(modelObj, newmodel));
4875 
4876   // save updated model to file
4877   if (saveGeom == PETSC_TRUE && stpName != NULL) PetscCall(EG_saveModel(newmodel, stpName));
4878 
4879   // Inflate Mesh to EGADS Model
4880   if (autoInflate == PETSC_TRUE) PetscCall(DMPlexInflateToGeomModel(dm, PETSC_TRUE));
4881   PetscFunctionReturn(PETSC_SUCCESS);
4882 #else
4883   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "This method requires EGADS support. Reconfigure using --download-egads");
4884 #endif
4885 }
4886 
4887 /*@C
4888   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.
4889 
4890   Collective
4891 
4892   Input Parameter:
4893 . dm - The DM object representing the mesh with PetscContainer containing an EGADS geometry model
4894 
4895   Level: intermediate
4896 
4897 .seealso: `DMPLEX`, `DMCreate()`, `DMPlexCreateGeom()`, `DMPlexGeomDataAndGrads()`
4898 @*/
4899 PetscErrorCode DMPlexGetGeomModelTUV(DM dm)
4900 {
4901 #if defined(PETSC_HAVE_EGADS)
4902   /* EGADS Variables */
4903   ego    model, geom, body, face, edge;
4904   ego   *bodies;
4905   int    Nb, oclass, mtype, *senses;
4906   double result[4];
4907   /* PETSc Variables */
4908   DM             cdm;
4909   PetscContainer modelObj;
4910   DMLabel        bodyLabel, faceLabel, edgeLabel, vertexLabel;
4911   Vec            coordinates;
4912   PetscScalar   *coords;
4913   PetscInt       bodyID, faceID, edgeID, vertexID;
4914   PetscInt       cdim, vStart, vEnd, v;
4915   PetscBool      islite = PETSC_FALSE;
4916 #endif
4917 
4918   PetscFunctionBegin;
4919 #if defined(PETSC_HAVE_EGADS)
4920   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
4921   if (!modelObj) {
4922     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
4923     islite = PETSC_TRUE;
4924   }
4925   if (!modelObj) PetscFunctionReturn(0);
4926 
4927   PetscCall(DMGetCoordinateDim(dm, &cdim));
4928   PetscCall(DMGetCoordinateDM(dm, &cdm));
4929   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
4930   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
4931   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
4932   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
4933   PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));
4934 
4935   PetscCall(PetscContainerGetPointer(modelObj, (void **)&model));
4936 
4937   if (islite) PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
4938   else PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
4939 
4940   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
4941   PetscCall(VecGetArrayWrite(coordinates, &coords));
4942 
4943   // Define t, u, v arrays to be stored in a PetscContainer after populated
4944   PetscScalar *t_point, *u_point, *v_point;
4945   PetscCall(PetscMalloc1(vEnd - vStart, &t_point));
4946   PetscCall(PetscMalloc1(vEnd - vStart, &u_point));
4947   PetscCall(PetscMalloc1(vEnd - vStart, &v_point));
4948 
4949   for (v = vStart; v < vEnd; ++v) {
4950     PetscScalar *vcoords;
4951 
4952     PetscCall(DMLabelGetValue(bodyLabel, v, &bodyID));
4953     PetscCall(DMLabelGetValue(faceLabel, v, &faceID));
4954     PetscCall(DMLabelGetValue(edgeLabel, v, &edgeID));
4955     PetscCall(DMLabelGetValue(vertexLabel, v, &vertexID));
4956 
4957     // TODO Figure out why this is unknown sometimes
4958     if (bodyID < 0 && Nb == 1) bodyID = 0;
4959     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);
4960     body = bodies[bodyID];
4961 
4962     PetscCall(DMPlexPointLocalRef(cdm, v, coords, (void *)&vcoords));
4963     if (edgeID > 0) {
4964       /* Snap to EDGE at nearest location */
4965       double params[1];
4966 
4967       if (islite) {
4968         PetscCall(EGlite_objectBodyTopo(body, EDGE, edgeID, &edge));
4969         PetscCall(EGlite_invEvaluate(edge, vcoords, params, result));
4970       } // Get (t) of nearest point on EDGE
4971       else {
4972         PetscCall(EG_objectBodyTopo(body, EDGE, edgeID, &edge));
4973         PetscCall(EG_invEvaluate(edge, vcoords, params, result));
4974       } // Get (t) of nearest point on EDGE
4975 
4976       t_point[v - vStart] = params[0];
4977       u_point[v - vStart] = 0.0;
4978       v_point[v - vStart] = 0.0;
4979     } else if (faceID > 0) {
4980       /* Snap to FACE at nearest location */
4981       double params[2];
4982 
4983       if (islite) {
4984         PetscCall(EGlite_objectBodyTopo(body, FACE, faceID, &face));
4985         PetscCall(EGlite_invEvaluate(face, vcoords, params, result));
4986       } // Get (x,y,z) of nearest point on FACE
4987       else {
4988         PetscCall(EG_objectBodyTopo(body, FACE, faceID, &face));
4989         PetscCall(EG_invEvaluate(face, vcoords, params, result));
4990       } // Get (x,y,z) of nearest point on FACE
4991 
4992       t_point[v - vStart] = 0.0;
4993       u_point[v - vStart] = params[0];
4994       v_point[v - vStart] = params[1];
4995     } else {
4996       t_point[v - vStart] = 0.0;
4997       u_point[v - vStart] = 0.0;
4998       v_point[v - vStart] = 0.0;
4999     }
5000   }
5001   PetscCall(VecRestoreArrayWrite(coordinates, &coords));
5002   /* Clear out global coordinates */
5003   PetscCall(VecDestroy(&dm->coordinates[0].x));
5004 
5005   /* Store in PetscContainters */
5006   {
5007     PetscContainer t_pointObj, u_pointObj, v_pointObj;
5008 
5009     PetscCall(PetscObjectQuery((PetscObject)dm, "Point - Edge t Parameter", (PetscObject *)&t_pointObj));
5010     if (!t_pointObj) {
5011       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &t_pointObj));
5012       PetscCall(PetscContainerSetPointer(t_pointObj, t_point));
5013       PetscCall(PetscObjectCompose((PetscObject)dm, "Point - Edge t Parameter", (PetscObject)t_pointObj));
5014       PetscCall(PetscContainerSetCtxDestroy(t_pointObj, PetscCtxDestroyDefault));
5015       PetscCall(PetscContainerDestroy(&t_pointObj));
5016     } else {
5017       void *old;
5018 
5019       PetscCall(PetscContainerGetPointer(t_pointObj, &old));
5020       PetscCall(PetscFree(old));
5021       PetscCall(PetscContainerSetPointer(t_pointObj, t_point));
5022     }
5023 
5024     PetscCall(PetscObjectQuery((PetscObject)dm, "Point - Face u Parameter", (PetscObject *)&u_pointObj));
5025     if (!u_pointObj) {
5026       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &u_pointObj));
5027       PetscCall(PetscContainerSetPointer(u_pointObj, u_point));
5028       PetscCall(PetscObjectCompose((PetscObject)dm, "Point - Face u Parameter", (PetscObject)u_pointObj));
5029       PetscCall(PetscContainerSetCtxDestroy(u_pointObj, PetscCtxDestroyDefault));
5030       PetscCall(PetscContainerDestroy(&u_pointObj));
5031     } else {
5032       void *old;
5033 
5034       PetscCall(PetscContainerGetPointer(u_pointObj, &old));
5035       PetscCall(PetscFree(old));
5036       PetscCall(PetscContainerSetPointer(u_pointObj, u_point));
5037     }
5038 
5039     PetscCall(PetscObjectQuery((PetscObject)dm, "Point - Face v Parameter", (PetscObject *)&v_pointObj));
5040     if (!v_pointObj) {
5041       PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &v_pointObj));
5042       PetscCall(PetscContainerSetPointer(v_pointObj, v_point));
5043       PetscCall(PetscObjectCompose((PetscObject)dm, "Point - Face v Parameter", (PetscObject)v_pointObj));
5044       PetscCall(PetscContainerSetCtxDestroy(v_pointObj, PetscCtxDestroyDefault));
5045       PetscCall(PetscContainerDestroy(&v_pointObj));
5046     } else {
5047       void *old;
5048 
5049       PetscCall(PetscContainerGetPointer(v_pointObj, &old));
5050       PetscCall(PetscFree(old));
5051       PetscCall(PetscContainerSetPointer(v_pointObj, v_point));
5052     }
5053   }
5054 #endif
5055   PetscFunctionReturn(PETSC_SUCCESS);
5056 }
5057 
5058 /*@C
5059   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().
5060 
5061   Collective
5062 
5063   Input Parameter:
5064 . dm - The DM object representing the mesh with PetscContainer containing an EGADS geometry model
5065 
5066   Level: intermediate
5067 
5068   Note:
5069   The updated DM object inflated to the associated underlying geometry. This updates the [x, y, z] coordinates of DM points associated with geometry.
5070 
5071 .seealso: `DMPLEX`, `DMCreate()`, `DMPlexCreateGeom()`, `DMPlexGeomDataAndGrads()`, `DMPlexGetGeomModelTUV()`
5072 @*/
5073 PetscErrorCode DMPlexInflateToGeomModelUseTUV(DM dm)
5074 {
5075 #if defined(PETSC_HAVE_EGADS)
5076   /* EGADS Variables */
5077   ego    model, geom, body, face, edge, vertex;
5078   ego   *bodies;
5079   int    Nb, oclass, mtype, *senses;
5080   double result[18], params[2];
5081   /* PETSc Variables */
5082   DM             cdm;
5083   PetscContainer modelObj;
5084   PetscContainer t_pointObj, u_pointObj, v_pointObj;
5085   DMLabel        bodyLabel, faceLabel, edgeLabel, vertexLabel;
5086   Vec            coordinates;
5087   PetscScalar   *coords;
5088   PetscScalar   *t_point, *u_point, *v_point;
5089   PetscInt       bodyID, faceID, edgeID, vertexID;
5090   PetscInt       cdim, d, vStart, vEnd, v;
5091   PetscBool      islite = PETSC_FALSE;
5092 #endif
5093 
5094   PetscFunctionBegin;
5095 #if defined(PETSC_HAVE_EGADS)
5096   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5097   if (!modelObj) {
5098     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5099     islite = PETSC_TRUE;
5100   }
5101 
5102   PetscCall(PetscObjectQuery((PetscObject)dm, "Point - Edge t Parameter", (PetscObject *)&t_pointObj));
5103   PetscCall(PetscObjectQuery((PetscObject)dm, "Point - Face u Parameter", (PetscObject *)&u_pointObj));
5104   PetscCall(PetscObjectQuery((PetscObject)dm, "Point - Face v Parameter", (PetscObject *)&v_pointObj));
5105 
5106   if (!modelObj) PetscFunctionReturn(PETSC_SUCCESS);
5107   if (!t_pointObj) PetscFunctionReturn(PETSC_SUCCESS);
5108   if (!u_pointObj) PetscFunctionReturn(PETSC_SUCCESS);
5109   if (!v_pointObj) PetscFunctionReturn(PETSC_SUCCESS);
5110 
5111   PetscCall(DMGetCoordinateDim(dm, &cdim));
5112   PetscCall(DMGetCoordinateDM(dm, &cdm));
5113   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
5114   PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
5115   PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
5116   PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
5117   PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));
5118 
5119   PetscCall(PetscContainerGetPointer(t_pointObj, (void **)&t_point));
5120   PetscCall(PetscContainerGetPointer(u_pointObj, (void **)&u_point));
5121   PetscCall(PetscContainerGetPointer(v_pointObj, (void **)&v_point));
5122 
5123   PetscCall(PetscContainerGetPointer(modelObj, (void **)&model));
5124 
5125   if (islite) {
5126     PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
5127   } else {
5128     PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
5129   }
5130 
5131   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
5132   PetscCall(VecGetArrayWrite(coordinates, &coords));
5133 
5134   for (v = vStart; v < vEnd; ++v) {
5135     PetscScalar *vcoords;
5136 
5137     PetscCall(DMLabelGetValue(bodyLabel, v, &bodyID));
5138     PetscCall(DMLabelGetValue(faceLabel, v, &faceID));
5139     PetscCall(DMLabelGetValue(edgeLabel, v, &edgeID));
5140     PetscCall(DMLabelGetValue(vertexLabel, v, &vertexID));
5141 
5142     // TODO Figure out why this is unknown sometimes
5143     if (bodyID < 0 && Nb == 1) bodyID = 0;
5144     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);
5145     body = bodies[bodyID];
5146 
5147     PetscCall(DMPlexPointLocalRef(cdm, v, coords, (void *)&vcoords));
5148     if (vertexID > 0) {
5149       /* Snap to Vertices */
5150       if (islite) {
5151         PetscCall(EGlite_objectBodyTopo(body, NODE, vertexID, &vertex));
5152         PetscCall(EGlite_evaluate(vertex, NULL, result));
5153       } else {
5154         PetscCall(EG_objectBodyTopo(body, NODE, vertexID, &vertex));
5155         PetscCall(EG_evaluate(vertex, NULL, result));
5156       }
5157       for (d = 0; d < cdim; ++d) vcoords[d] = result[d];
5158     } else if (edgeID > 0) {
5159       /* Snap to EDGE */
5160       params[0] = t_point[v - vStart];
5161       if (islite) {
5162         PetscCall(EGlite_objectBodyTopo(body, EDGE, edgeID, &edge));
5163         PetscCall(EGlite_evaluate(edge, params, result));
5164       } else {
5165         PetscCall(EG_objectBodyTopo(body, EDGE, edgeID, &edge));
5166         PetscCall(EG_evaluate(edge, params, result));
5167       }
5168       for (d = 0; d < cdim; ++d) vcoords[d] = result[d];
5169     } else if (faceID > 0) {
5170       /* Snap to FACE */
5171       params[0] = u_point[v - vStart];
5172       params[1] = v_point[v - vStart];
5173       if (islite) {
5174         PetscCall(EGlite_objectBodyTopo(body, FACE, faceID, &face));
5175         PetscCall(EGlite_evaluate(face, params, result));
5176       } else {
5177         PetscCall(EG_objectBodyTopo(body, FACE, faceID, &face));
5178         PetscCall(EG_evaluate(face, params, result));
5179       }
5180       for (d = 0; d < cdim; ++d) vcoords[d] = result[d];
5181     }
5182   }
5183   PetscCall(VecRestoreArrayWrite(coordinates, &coords));
5184   /* Clear out global coordinates */
5185   PetscCall(VecDestroy(&dm->coordinates[0].x));
5186 #endif
5187   PetscFunctionReturn(PETSC_SUCCESS);
5188 }
5189 
5190 /*@
5191   DMPlexInflateToGeomModel - Wrapper function allowing two methods for inflating refined meshes to the underlying geometric domain.
5192 
5193   Collective
5194 
5195   Input Parameters:
5196 + dm     - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5197 - useTUV - PetscBool indicating if the user would like to inflate the DMPlex to the underlying geometry
5198            using (t) for nodes on EDGEs and (u, v) for nodes on FACEs or using the nodes (x, y, z) coordinates
5199            and shortest distance routine.
5200             If useTUV = PETSC_TRUE, use the (t) or (u, v) parameters to inflate the DMPlex to the CAD geometry.
5201             If useTUV = PETSC_FALSE, use the nodes (x, y, z) coordinates and the shortest disctance routine.
5202 
5203   Notes:
5204   DM with nodal coordinates modified so that they lie on the EDGEs and FACEs of the underlying geometry.
5205 
5206   (t) and (u, v) parameters for all DMPlex nodes on EDGEs and FACEs are stored in arrays within PetscContainers attached to the DM.
5207   The containers have names "Point - Edge t Parameter", "Point - Face u Parameter", and "Point - Face v Parameter".
5208   The arrays are organized by Point 0-based ID (i.e. [v-vstart] as defined in the DMPlex.
5209 
5210   Level: intermediate
5211 
5212 .seealso: `DMPlexGetGeomModelTUV()`, `DMPlexInflateToGeomModelUseTUV()`, `DMPlexInflateToGeomModelUseXYZ()`
5213 @*/
5214 PetscErrorCode DMPlexInflateToGeomModel(DM dm, PetscBool useTUV)
5215 {
5216   PetscFunctionBeginHot;
5217   if (useTUV) {
5218     PetscCall(DMPlexGetGeomModelTUV(dm));
5219     PetscCall(DMPlexInflateToGeomModelUseTUV(dm));
5220   } else {
5221     PetscCall(DMPlexInflateToGeomModelUseXYZ(dm));
5222   }
5223   PetscFunctionReturn(PETSC_SUCCESS);
5224 }
5225 
5226 #ifdef PETSC_HAVE_EGADS
5227 /*@C
5228   DMPlexGetGeomModelBodies - Returns an array of PetscGeom BODY objects attached to the referenced geomtric model entity as well as the number of BODYs.
5229 
5230   Collective
5231 
5232   Input Parameter:
5233 . dm - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5234 
5235   Output Parameters:
5236 + bodies    - Array of PetscGeom BODY objects referenced by the geometric model.
5237 - numBodies - Number of BODYs referenced by the geometric model. Also the size of **bodies array.
5238 
5239   Level: intermediate
5240 
5241 .seealso:
5242 @*/
5243 PetscErrorCode DMPlexGetGeomModelBodies(DM dm, PetscGeom **bodies, PetscInt *numBodies)
5244 {
5245   PetscFunctionBeginHot;
5246   PetscContainer modelObj;
5247   PetscBool      islite = PETSC_FALSE;
5248   ego            model, geom;
5249   int            oclass, mtype;
5250   int           *senses;
5251 
5252   /* Determine which type of EGADS model is attached to the DM */
5253   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5254   if (!modelObj) {
5255     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5256     islite = PETSC_TRUE;
5257   }
5258 
5259   // Get attached EGADS or EGADSlite model (pointer)
5260   PetscCall(PetscContainerGetPointer(modelObj, (void **)&model));
5261 
5262   if (islite) {
5263     PetscCall(EGlite_getTopology(model, &geom, &oclass, &mtype, NULL, numBodies, bodies, &senses));
5264   } else {
5265     PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, numBodies, bodies, &senses));
5266   }
5267   PetscFunctionReturn(PETSC_SUCCESS);
5268 }
5269 
5270 /*@C
5271   DMPlexGetGeomModelBodyShells - Returns an array of PetscGeom SHELL objects attached to the referenced BODY geomtric entity as well as the number of SHELLs.
5272 
5273   Collective
5274 
5275   Input Parameters:
5276 + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5277 - body - PetscGeom BODY object containing the SHELL objects of interest.
5278 
5279   Output Parameters:
5280 + shells    - Array of PetscGeom SHELL objects referenced by the PetscGeom BODY object
5281 - numShells - Number of SHELLs referenced by the PetscGeom BODY object. Also the size of **shells array.
5282 
5283   Level: intermediate
5284 
5285 .seealso:
5286 @*/
5287 PetscErrorCode DMPlexGetGeomModelBodyShells(DM dm, PetscGeom body, PetscGeom **shells, PetscInt *numShells)
5288 {
5289   PetscFunctionBeginHot;
5290   #ifdef PETSC_HAVE_EGADS
5291   PetscContainer modelObj;
5292   PetscBool      islite = PETSC_FALSE;
5293 
5294   /* Determine which type of EGADS model is attached to the DM */
5295   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5296   if (!modelObj) {
5297     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5298     islite = PETSC_TRUE;
5299   }
5300 
5301   if (islite) {
5302     PetscCall(EGlite_getBodyTopos(body, NULL, SHELL, numShells, shells));
5303   } else {
5304     PetscCall(EG_getBodyTopos(body, NULL, SHELL, numShells, shells));
5305   }
5306   #endif
5307   PetscFunctionReturn(PETSC_SUCCESS);
5308 }
5309 
5310 /*@C
5311   DMPlexGetGeomModelBodyFaces - Returns an array of PetscGeom FACE objects attached to the referenced BODY geomtric entity as well as the number of FACEs.
5312 
5313   Collective
5314 
5315   Input Parameters:
5316 + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5317 - body - PetscGeom BODY object containing the FACE objects of interest.
5318 
5319   Output Parameters:
5320 + faces    - Array of PetscGeom FACE objects referenced by the PetscGeom BODY object
5321 - numFaces - Number of FACEs referenced by the PetscGeom BODY object. Also the size of **faces array.
5322 
5323   Level: intermediate
5324 
5325 .seealso:
5326 @*/
5327 PetscErrorCode DMPlexGetGeomModelBodyFaces(DM dm, PetscGeom body, PetscGeom **faces, PetscInt *numFaces)
5328 {
5329   PetscFunctionBeginHot;
5330   #ifdef PETSC_HAVE_EGADS
5331   PetscContainer modelObj;
5332   PetscBool      islite = PETSC_FALSE;
5333 
5334   /* Determine which type of EGADS model is attached to the DM */
5335   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5336   if (!modelObj) {
5337     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5338     islite = PETSC_TRUE;
5339   }
5340 
5341   if (islite) {
5342     PetscCall(EGlite_getBodyTopos(body, NULL, FACE, numFaces, faces));
5343   } else {
5344     PetscCall(EG_getBodyTopos(body, NULL, FACE, numFaces, faces));
5345   }
5346   #endif
5347   PetscFunctionReturn(PETSC_SUCCESS);
5348 }
5349 
5350 /*@C
5351   DMPlexGetGeomModelBodyLoops - Returns an array of PetscGeom Loop objects attached to the referenced BODY geomtric entity as well as the number of LOOPs.
5352 
5353   Collective
5354 
5355   Input Parameters:
5356 + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5357 - body - PetscGeom BODY object containing the LOOP objects of interest.
5358 
5359   Output Parameters:
5360 + loops    - Array of PetscGeom FACE objects referenced by the PetscGeom SHELL object
5361 - numLoops - Number of LOOPs referenced by the PetscGeom BODY object. Also the size of **loops array.
5362 
5363   Level: intermediate
5364 
5365 .seealso:
5366 @*/
5367 PetscErrorCode DMPlexGetGeomModelBodyLoops(DM dm, PetscGeom body, PetscGeom **loops, PetscInt *numLoops)
5368 {
5369   PetscFunctionBeginHot;
5370   #ifdef PETSC_HAVE_EGADS
5371   PetscContainer modelObj;
5372   PetscBool      islite = PETSC_FALSE;
5373 
5374   /* Determine which type of EGADS model is attached to the DM */
5375   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5376   if (!modelObj) {
5377     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5378     islite = PETSC_TRUE;
5379   }
5380 
5381   if (islite) {
5382     PetscCall(EGlite_getBodyTopos(body, NULL, LOOP, numLoops, loops));
5383   } else {
5384     PetscCall(EG_getBodyTopos(body, NULL, LOOP, numLoops, loops));
5385   }
5386   #endif
5387   PetscFunctionReturn(PETSC_SUCCESS);
5388 }
5389 
5390 /*@C
5391   DMPlexGetGeomModelShellFaces - Returns an array of PetscGeom FACE objects attached to the referenced SHELL geomtric entity as well as the number of FACEs.
5392 
5393   Collective
5394 
5395   Input Parameters:
5396 + dm    - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5397 . body  - PetscGeom BODY object containing the FACE objects of interest.
5398 - shell - PetscGeom SHELL object with FACEs of interest.
5399 
5400   Output Parameters:
5401 + faces    - Array of PetscGeom FACE objects referenced by the PetscGeom SHELL object
5402 - numFaces - Number of FACEs referenced by the PetscGeom SHELL object. Also the size of **faces array.
5403 
5404   Level: intermediate
5405 
5406 .seealso:
5407 @*/
5408 PetscErrorCode DMPlexGetGeomModelShellFaces(DM dm, PetscGeom body, PetscGeom shell, PetscGeom **faces, PetscInt *numFaces)
5409 {
5410   PetscFunctionBeginHot;
5411   #ifdef PETSC_HAVE_EGADS
5412   PetscContainer modelObj;
5413   PetscBool      islite = PETSC_FALSE;
5414 
5415   /* Determine which type of EGADS model is attached to the DM */
5416   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5417   if (!modelObj) {
5418     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5419     islite = PETSC_TRUE;
5420   }
5421 
5422   if (islite) {
5423     PetscCall(EGlite_getBodyTopos(body, shell, FACE, numFaces, faces));
5424   } else {
5425     PetscCall(EG_getBodyTopos(body, shell, FACE, numFaces, faces));
5426   }
5427   #endif
5428   PetscFunctionReturn(PETSC_SUCCESS);
5429 }
5430 
5431 /*@C
5432   DMPlexGetGeomModelFaceLoops - Returns an array of PetscGeom LOOP objects attached to the referenced FACE geomtric entity as well as the number of LOOPs.
5433 
5434   Collective
5435 
5436   Input Parameters:
5437 + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5438 . body - PetscGeom BODY object containing the LOOP objects of interest.
5439 - face - PetscGeom FACE object with LOOPs of interest.
5440 
5441   Output Parameters:
5442 + loops    - Array of PetscGeom LOOP objects referenced by the PetscGeom FACE object
5443 - numLoops - Number of LOOPs referenced by the PetscGeom FACE object. Also the size of **loops array.
5444 
5445   Level: intermediate
5446 
5447 .seealso:
5448 @*/
5449 PetscErrorCode DMPlexGetGeomModelFaceLoops(DM dm, PetscGeom body, PetscGeom face, PetscGeom **loops, PetscInt *numLoops)
5450 {
5451   PetscFunctionBeginHot;
5452   #ifdef PETSC_HAVE_EGADS
5453   PetscContainer modelObj;
5454   PetscBool      islite = PETSC_FALSE;
5455 
5456   /* Determine which type of EGADS model is attached to the DM */
5457   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5458   if (!modelObj) {
5459     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5460     islite = PETSC_TRUE;
5461   }
5462 
5463   if (islite) {
5464     PetscCall(EGlite_getBodyTopos(body, face, LOOP, numLoops, loops));
5465   } else {
5466     PetscCall(EG_getBodyTopos(body, face, LOOP, numLoops, loops));
5467   }
5468   #endif
5469   PetscFunctionReturn(PETSC_SUCCESS);
5470 }
5471 
5472 /*@C
5473   DMPlexGetGeomModelFaceEdges - Returns an array of PetscGeom EDGE objects attached to the referenced FACE geomtric entity as well as the number of EDGEs.
5474 
5475   Collective
5476 
5477   Input Parameters:
5478 + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5479 . body - PetscGeom Body object containing the EDGE objects of interest.
5480 - face - PetscGeom FACE object with EDGEs of interest.
5481 
5482   Output Parameters:
5483 + edges    - Array of PetscGeom EDGE objects referenced by the PetscGeom FACE object
5484 - numEdges - Number of EDGEs referenced by the PetscGeom FACE object. Also the size of **edges array.
5485 
5486   Level: intermediate
5487 
5488 .seealso:
5489 @*/
5490 PetscErrorCode DMPlexGetGeomModelFaceEdges(DM dm, PetscGeom body, PetscGeom face, PetscGeom **edges, PetscInt *numEdges)
5491 {
5492   PetscFunctionBeginHot;
5493   #ifdef PETSC_HAVE_EGADS
5494   PetscContainer modelObj;
5495   PetscBool      islite = PETSC_FALSE;
5496 
5497   /* Determine which type of EGADS model is attached to the DM */
5498   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5499   if (!modelObj) {
5500     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5501     islite = PETSC_TRUE;
5502   }
5503 
5504   if (islite) {
5505     PetscCall(EGlite_getBodyTopos(body, face, EDGE, numEdges, edges));
5506   } else {
5507     PetscCall(EG_getBodyTopos(body, face, EDGE, numEdges, edges));
5508   }
5509   #endif
5510   PetscFunctionReturn(PETSC_SUCCESS);
5511 }
5512 
5513 /*@C
5514   DMPlexGetGeomModelBodyEdges - Returns an array of PetscGeom EDGE objects attached to the referenced BODY geomtric entity as well as the number of EDGEs.
5515 
5516   Collective
5517 
5518   Input Parameters:
5519 + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5520 - body - PetscGeom body object of interest.
5521 
5522   Output Parameters:
5523 + edges    - Array of PetscGeom EDGE objects referenced by the PetscGeom BODY object
5524 - numEdges - Number of EDGEs referenced by the PetscGeom BODY object. Also the size of **edges array.
5525 
5526   Level: intermediate
5527 
5528 .seealso:
5529 @*/
5530 PetscErrorCode DMPlexGetGeomModelBodyEdges(DM dm, PetscGeom body, PetscGeom **edges, PetscInt *numEdges)
5531 {
5532   PetscFunctionBeginHot;
5533   #ifdef PETSC_HAVE_EGADS
5534   PetscContainer modelObj;
5535   PetscBool      islite = PETSC_FALSE;
5536 
5537   /* Determine which type of EGADS model is attached to the DM */
5538   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5539   if (!modelObj) {
5540     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5541     islite = PETSC_TRUE;
5542   }
5543 
5544   if (islite) {
5545     PetscCall(EGlite_getBodyTopos(body, NULL, EDGE, numEdges, edges));
5546   } else {
5547     PetscCall(EG_getBodyTopos(body, NULL, EDGE, numEdges, edges));
5548   }
5549   #endif
5550   PetscFunctionReturn(PETSC_SUCCESS);
5551 }
5552 
5553 /*@C
5554   DMPlexGetGeomModelBodyNodes - Returns an array of PetscGeom NODE objects attached to the referenced BODY geomtric entity as well as the number of NODES.
5555 
5556   Collective
5557 
5558   Input Parameters:
5559 + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5560 - body - PetscGeom body object of interest.
5561 
5562   Output Parameters:
5563 + nodes    - Array of PetscGeom NODE objects referenced by the PetscGeom BODY object
5564 - numNodes - Number of NODEs referenced by the PetscGeom BODY object. Also the size of **nodes array.
5565 
5566   Level: intermediate
5567 
5568 .seealso:
5569 @*/
5570 PetscErrorCode DMPlexGetGeomModelBodyNodes(DM dm, PetscGeom body, PetscGeom **nodes, PetscInt *numNodes)
5571 {
5572   PetscFunctionBeginHot;
5573   #ifdef PETSC_HAVE_EGADS
5574   PetscContainer modelObj;
5575   PetscBool      islite = PETSC_FALSE;
5576 
5577   /* Determine which type of EGADS model is attached to the DM */
5578   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5579   if (!modelObj) {
5580     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5581     islite = PETSC_TRUE;
5582   }
5583 
5584   if (islite) {
5585     PetscCall(EGlite_getBodyTopos(body, NULL, NODE, numNodes, nodes));
5586   } else {
5587     PetscCall(EG_getBodyTopos(body, NULL, NODE, numNodes, nodes));
5588   }
5589   #endif
5590   PetscFunctionReturn(PETSC_SUCCESS);
5591 }
5592 
5593 /*@C
5594   DMPlexGetGeomModelEdgeNodes - Returns an array of PetscGeom NODE objects attached to the referenced EDGE geomtric entity as well as the number of NODES.
5595 
5596   Collective
5597 
5598   Input Parameters:
5599 + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5600 . body - PetscGeom body object containing the EDGE object of interest.
5601 - edge - PetscGeom EDGE object with NODEs of interest.
5602 
5603   Output Parameters:
5604 + nodes    - Array of PetscGeom NODE objects referenced by the PetscGeom EDGE object
5605 - numNodes - Number of Nodes referenced by the PetscGeom EDGE object. Also the size of **nodes array.
5606 
5607   Level: intermediate
5608 
5609 .seealso:
5610 @*/
5611 PetscErrorCode DMPlexGetGeomModelEdgeNodes(DM dm, PetscGeom body, PetscGeom edge, PetscGeom **nodes, PetscInt *numNodes)
5612 {
5613   PetscFunctionBeginHot;
5614   #ifdef PETSC_HAVE_EGADS
5615   PetscContainer modelObj;
5616   PetscBool      islite = PETSC_FALSE;
5617 
5618   /* Determine which type of EGADS model is attached to the DM */
5619   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5620   if (!modelObj) {
5621     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5622     islite = PETSC_TRUE;
5623   }
5624 
5625   if (islite) {
5626     PetscCall(EGlite_getBodyTopos(body, edge, NODE, numNodes, nodes));
5627   } else {
5628     PetscCall(EG_getBodyTopos(body, edge, NODE, numNodes, nodes));
5629   }
5630   #endif
5631   PetscFunctionReturn(PETSC_SUCCESS);
5632 }
5633 
5634 /*@C
5635   DMPlexGetGeomID - Returns ID number of the entity in the geometric (CAD) model
5636 
5637   Collective
5638 
5639   Input Parameters:
5640 + dm      - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5641 . body    - PetscGeom body object containing the lower level entity the ID number is being requested.
5642 - topoObj - PetscGeom SHELL, FACE, LOOP, EDGE, or NODE object for which ID number is being requested.
5643 
5644   Output Parameter:
5645 . id - ID number of the entity
5646 
5647   Level: intermediate
5648 
5649 .seealso:
5650 @*/
5651 PetscErrorCode DMPlexGetGeomID(DM dm, PetscGeom body, PetscGeom topoObj, PetscInt *id)
5652 {
5653   PetscFunctionBeginHot;
5654   #ifdef PETSC_HAVE_EGADS
5655   PetscContainer modelObj;
5656   PetscBool      islite = PETSC_FALSE;
5657   int            topoID;
5658 
5659   /* Determine which type of EGADS model is attached to the DM */
5660   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5661   if (!modelObj) {
5662     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5663     islite = PETSC_TRUE;
5664   }
5665 
5666   // Get Topology Object's ID
5667   if (islite) {
5668     topoID = EGlite_indexBodyTopo(body, topoObj);
5669   } else {
5670     topoID = EG_indexBodyTopo(body, topoObj);
5671   }
5672 
5673   *id = topoID;
5674   #endif
5675   PetscFunctionReturn(PETSC_SUCCESS);
5676 }
5677 
5678 /*@C
5679   DMPlexGetGeomObject - Returns Geometry Object using the objects ID in the geometric (CAD) model
5680 
5681   Collective
5682 
5683   Input Parameters:
5684 + dm       - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5685 . body     - PetscGeom body object containing the lower level entity the referenced by the ID.
5686 . geomType - Keyword SHELL, FACE, LOOP, EDGE, or NODE of the geometry type for which ID number is being requested.
5687 - geomID   - ID number of the geometry entity being requested.
5688 
5689   Output Parameter:
5690 . geomObj - Geometry Object referenced by the ID number requested.
5691 
5692   Level: intermediate
5693 
5694 .seealso:
5695 @*/
5696 PetscErrorCode DMPlexGetGeomObject(DM dm, PetscGeom body, PetscInt geomType, PetscInt geomID, PetscGeom *geomObj)
5697 {
5698   PetscFunctionBeginHot;
5699   #ifdef PETSC_HAVE_EGADS
5700   PetscContainer modelObj;
5701   PetscBool      islite = PETSC_FALSE;
5702 
5703   /* Determine which type of EGADS model is attached to the DM */
5704   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5705   if (!modelObj) {
5706     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5707     islite = PETSC_TRUE;
5708   }
5709 
5710   // Get Topology Object's ID
5711   if (islite) {
5712     PetscCall(EGlite_objectBodyTopo(body, geomType, geomID, geomObj));
5713   } else {
5714     PetscCall(EG_objectBodyTopo(body, geomType, geomID, geomObj));
5715   }
5716   #endif
5717   PetscFunctionReturn(PETSC_SUCCESS);
5718 }
5719 
5720 /*@C
5721   DMPlexGetGeomFaceNumOfControlPoints - Returns the total number of Control Points (and associated Weights) defining a FACE of a Geometry
5722 
5723   Not collective
5724 
5725   Input Parameters:
5726 + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5727 - face - PetscGeom FACE object
5728 
5729   Output Parameter:
5730 . numCntrlPnts - Number of Control Points (and Weights) defining the FACE
5731 
5732   Level: intermediate
5733 
5734 .seealso:
5735 @*/
5736 PetscErrorCode DMPlexGetGeomFaceNumOfControlPoints(DM dm, PetscGeom face, PetscInt *numCntrlPnts)
5737 {
5738   PetscFunctionBeginHot;
5739   #ifdef PETSC_HAVE_EGADS
5740   PetscContainer modelObj;
5741   PetscBool      islite = PETSC_FALSE;
5742   PetscGeom      geom, gRef;
5743   PetscGeom     *lobjs;
5744   int            Nl, oclass, mtype, goclass, gmtype;
5745   int           *lsenses, *gpinfo;
5746   double        *gprv;
5747 
5748   /* Determine which type of EGADS model is attached to the DM */
5749   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5750   if (!modelObj) {
5751     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5752     islite = PETSC_TRUE;
5753   }
5754 
5755   // Get Total Number of Control Points on FACE
5756   if (islite) {
5757     PetscCall(EGlite_getTopology(face, &geom, &oclass, &mtype, NULL, &Nl, &lobjs, &lsenses));
5758     PetscCall(EGlite_getGeometry(geom, &goclass, &gmtype, &gRef, &gpinfo, &gprv));
5759   } else {
5760     PetscCall(EG_getTopology(face, &geom, &oclass, &mtype, NULL, &Nl, &lobjs, &lsenses));
5761     PetscCall(EG_getGeometry(geom, &goclass, &gmtype, &gRef, &gpinfo, &gprv));
5762   }
5763 
5764   *numCntrlPnts = gpinfo[2] * gpinfo[5];
5765   #endif
5766   PetscFunctionReturn(PETSC_SUCCESS);
5767 }
5768 
5769 /*@C
5770   DMPlexGetGeomBodyMassProperties - Returns the Volume, Surface Area, Center of Gravity, and Inertia about the Body's Center of Gravity
5771 
5772   Not collective
5773 
5774   Input Parameters:
5775 + dm   - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5776 - body - PetscGeom BODY object
5777 
5778   Output Parameters:
5779 + volume           - Volume of the CAD Body attached to the DM Plex
5780 . surfArea         - Surface Area of the CAD Body attached to the DM Plex
5781 . centerOfGravity  - Array with the Center of Gravity coordinates of the CAD Body attached to the DM Plex [x, y, z]
5782 . COGszie          - Size of centerOfGravity[] Array
5783 . inertiaMatrixCOG - Array containing the Inertia about the Body's Center of Gravity [Ixx, Ixy, Ixz, Iyx, Iyy, Iyz, Izx, Izy, Izz]
5784 - IMCOGsize        - Size of inertiaMatrixCOG[] Array
5785 
5786   Level: intermediate
5787 
5788 .seealso:
5789 @*/
5790 PetscErrorCode DMPlexGetGeomBodyMassProperties(DM dm, PetscGeom body, PetscScalar *volume, PetscScalar *surfArea, PetscScalar **centerOfGravity, PetscInt *COGsize, PetscScalar **inertiaMatrixCOG, PetscInt *IMCOGsize)
5791 {
5792   PetscFunctionBeginHot;
5793   #ifdef PETSC_HAVE_EGADS
5794   PetscContainer modelObj;
5795   PetscBool      islite = PETSC_FALSE;
5796   PetscScalar    geomData[14];
5797 
5798   /* Determine which type of EGADS model is attached to the DM */
5799   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5800   if (!modelObj) {
5801     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5802     islite = PETSC_TRUE;
5803     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");
5804   }
5805 
5806   if (islite) {
5807     PetscCall(PetscPrintf(PETSC_COMM_SELF, " WARNING!! This functionality is not supported for EGADSlite files. \n"));
5808     PetscCall(PetscPrintf(PETSC_COMM_SELF, " All returned values are equal to 0 \n"));
5809   } else {
5810     PetscCall(EG_getMassProperties(body, geomData));
5811   }
5812 
5813   PetscCall(PetscMalloc2(3, centerOfGravity, 9, inertiaMatrixCOG));
5814 
5815   if (!islite) {
5816     *volume   = geomData[0];
5817     *surfArea = geomData[1];
5818     for (int ii = 2; ii < 5; ++ii) { (*centerOfGravity)[ii - 2] = geomData[ii]; }
5819     *COGsize = 3;
5820     for (int ii = 5; ii < 14; ++ii) { (*inertiaMatrixCOG)[ii - 5] = geomData[ii]; }
5821     *IMCOGsize = 9;
5822   } else {
5823     *volume   = 0.;
5824     *surfArea = 0.;
5825     for (int ii = 2; ii < 5; ++ii) { (*centerOfGravity)[ii - 2] = 0.; }
5826     *COGsize = 0;
5827     for (int ii = 5; ii < 14; ++ii) { (*inertiaMatrixCOG)[ii - 5] = 0.; }
5828     *IMCOGsize = 0;
5829   }
5830   #endif
5831   PetscFunctionReturn(PETSC_SUCCESS);
5832 }
5833 
5834 PetscErrorCode DMPlexRestoreGeomBodyMassProperties(DM dm, PetscGeom body, PetscScalar *volume, PetscScalar *surfArea, PetscScalar **centerOfGravity, PetscInt *COGsize, PetscScalar **inertiaMatrixCOG, PetscInt *IMCOGsize)
5835 {
5836   PetscFunctionBegin;
5837   PetscCall(PetscFree2(*centerOfGravity, *inertiaMatrixCOG));
5838   PetscFunctionReturn(PETSC_SUCCESS);
5839 }
5840 
5841 /*@C
5842   DMPlexFreeGeomObject - Frees PetscGeom Objects
5843 
5844   Not collective
5845 
5846   Input Parameters:
5847 + dm      - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5848 - geomObj - PetscGeom object
5849 
5850   Level: intermediate
5851 
5852 .seealso:
5853 @*/
5854 PetscErrorCode DMPlexFreeGeomObject(DM dm, PetscGeom *geomObj)
5855 {
5856   PetscFunctionBeginHot;
5857   #ifdef PETSC_HAVE_EGADS
5858   PetscContainer modelObj;
5859   PetscBool      islite = PETSC_FALSE;
5860 
5861   /* Determine which type of EGADS model is attached to the DM */
5862   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5863   if (!modelObj) {
5864     PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj));
5865     islite = PETSC_TRUE;
5866   }
5867 
5868   if (islite) {
5869     EGlite_free(geomObj);
5870   } else {
5871     EG_free(geomObj);
5872   }
5873   #endif
5874   PetscFunctionReturn(PETSC_SUCCESS);
5875 }
5876 
5877 /*@C
5878   DMPlexGetGeomCntrlPntAndWeightData - Gets Control Point and Associated Weight Data for the Geometry attached to the DMPlex
5879 
5880   Not collective
5881 
5882   Input Parameter:
5883 . dm - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5884 
5885   Output Parameters:
5886 + cpHashTable       - Hash Table containing the relationship between FACE ID and Control Point IDs.
5887 . cpCoordDataLength - Length of cpCoordData Array.
5888 . cpCoordData       - Array holding the Geometry Control Point Coordinate Data.
5889 . maxNumEquiv       - Maximum Number of Equivalent Control Points (Control Points with the same coordinates but different IDs).
5890 . 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
5891 . wHashTable        - Hash Table containing the relationship between FACE ID and Control Point Weight.
5892 . wDataLength       - Length of wData Array.
5893 - wData             - Array holding the Weight for an associated Geometry Control Point.
5894 
5895   Note:
5896   Must Call DMPLexGeomDataAndGrads() before calling this function.
5897 
5898   Level: intermediate
5899 
5900 .seealso:
5901 @*/
5902 PetscErrorCode DMPlexGetGeomCntrlPntAndWeightData(DM dm, PetscHMapI *cpHashTable, PetscInt *cpCoordDataLength, PetscScalar **cpCoordData, PetscInt *maxNumEquiv, Mat *cpEquiv, PetscHMapI *wHashTable, PetscInt *wDataLength, PetscScalar **wData)
5903 {
5904   PetscContainer modelObj, cpHashTableObj, wHashTableObj, cpCoordDataLengthObj, wDataLengthObj, maxNumRelateObj;
5905   Vec            cntrlPtCoordsVec, cntrlPtWeightsVec;
5906   PetscInt      *cpCoordDataLengthPtr, *wDataLengthPtr, *maxNumEquivPtr;
5907   PetscHMapI     cpHashTableTemp, wHashTableTemp;
5908 
5909   PetscFunctionBeginHot;
5910   /* Determine which type of EGADS model is attached to the DM */
5911   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5912   if (!modelObj) { PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj)); }
5913 
5914   if (!modelObj) { PetscFunctionReturn(PETSC_SUCCESS); }
5915 
5916   // Look to see if DM has Container for Geometry Control Point Data
5917   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Hash Table", (PetscObject *)&cpHashTableObj));
5918   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Coordinates", (PetscObject *)&cntrlPtCoordsVec));
5919   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Coordinate Data Length", (PetscObject *)&cpCoordDataLengthObj));
5920   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weights Hash Table", (PetscObject *)&wHashTableObj));
5921   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight Data", (PetscObject *)&cntrlPtWeightsVec));
5922   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight Data Length", (PetscObject *)&wDataLengthObj));
5923   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Equivalancy Matrix", (PetscObject *)cpEquiv));
5924   PetscCall(PetscObjectQuery((PetscObject)dm, "Maximum Number Control Point Equivalency", (PetscObject *)&maxNumRelateObj));
5925 
5926   // Get attached EGADS model Control Point and Weights Hash Tables and Data Arrays (pointer)
5927   PetscCall(PetscContainerGetPointer(cpHashTableObj, (void **)&cpHashTableTemp));
5928   PetscCall(PetscContainerGetPointer(cpCoordDataLengthObj, (void **)&cpCoordDataLengthPtr));
5929   PetscCall(PetscContainerGetPointer(wHashTableObj, (void **)&wHashTableTemp));
5930   PetscCall(PetscContainerGetPointer(wDataLengthObj, (void **)&wDataLengthPtr));
5931   PetscCall(PetscContainerGetPointer(maxNumRelateObj, (void **)&maxNumEquivPtr));
5932 
5933   *cpCoordDataLength = *cpCoordDataLengthPtr;
5934   *wDataLength       = *wDataLengthPtr;
5935   *maxNumEquiv       = *maxNumEquivPtr;
5936   *cpHashTable       = cpHashTableTemp;
5937   *wHashTable        = wHashTableTemp;
5938   PetscCall(VecGetArrayWrite(cntrlPtCoordsVec, cpCoordData));
5939   PetscCall(VecGetArrayWrite(cntrlPtWeightsVec, wData));
5940   PetscFunctionReturn(PETSC_SUCCESS);
5941 }
5942 
5943 PetscErrorCode DMPlexRestoreGeomCntrlPntAndWeightData(DM dm, PetscHMapI *cpHashTable, PetscInt *cpCoordDataLength, PetscScalar **cpCoordData, PetscInt *maxNumEquiv, Mat *cpEquiv, PetscHMapI *wHashTable, PetscInt *wDataLength, PetscScalar **wData)
5944 {
5945   Vec cntrlPtCoordsVec, cntrlPtWeightsVec;
5946 
5947   PetscFunctionBeginHot;
5948   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Coordinates", (PetscObject *)&cntrlPtCoordsVec));
5949   PetscCall(VecRestoreArrayWrite(cntrlPtCoordsVec, cpCoordData));
5950   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight Data", (PetscObject *)&cntrlPtWeightsVec));
5951   PetscCall(VecRestoreArrayWrite(cntrlPtWeightsVec, wData));
5952   PetscFunctionReturn(PETSC_SUCCESS);
5953 }
5954 
5955 /*@C
5956   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 .
5957 
5958   Not collective
5959 
5960   Input Parameter:
5961 . dm - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
5962 
5963   Output Parameters:
5964 + cpSurfGradHashTable - Hash Table Relating the Control Point ID to the the Row in the cpSurfGrad Matrix
5965 . 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.
5966 . cpArraySize         - The size of arrays gradSACP and gradVolCP and is equal to 3 * total number of Control Points in the Geometry
5967 . 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.
5968 . gradVolCP           - Array contianing the Volume Gradient with respect to Control Point Data. Data is arranged by Control Point ID * 3 where 3 is for the coordinate dimension.
5969 . wArraySize          - The size of arrayws gradSAW and gradVolW and is equal to the total number of Control Points in the Geometry.
5970 . gradSAW             - Array containing the Surface Area Gradient with respect to Control Point Weight. Data is arranged by Control Point ID.
5971 - gradVolW            - Array containing the Volume Gradient with respect to Control Point Weight. Data is arranged by Control Point ID.
5972 
5973   Notes:
5974   Must Call DMPLexGeomDataAndGrads() before calling this function.
5975 
5976   gradVolCP and gradVolW are only available when DMPlexGeomDataAndGrads() is called with fullGeomGrad = PETSC_TRUE.
5977 
5978   Level: intermediate
5979 
5980 .seealso: DMPlexGeomDataAndGrads
5981 @*/
5982 PetscErrorCode DMPlexGetGeomGradData(DM dm, PetscHMapI *cpSurfGradHashTable, Mat *cpSurfGrad, PetscInt *cpArraySize, PetscScalar **gradSACP, PetscScalar **gradVolCP, PetscInt *wArraySize, PetscScalar **gradSAW, PetscScalar **gradVolW)
5983 {
5984   PetscContainer modelObj, cpSurfGradHashTableObj, cpArraySizeObj, wArraySizeObj;
5985   Vec            gradSACPVec, gradVolCPVec, gradSAWVec, gradVolWVec;
5986   PetscInt      *cpArraySizePtr, *wArraySizePtr;
5987   PetscHMapI     cpSurfGradHashTableTemp;
5988 
5989   PetscFunctionBeginHot;
5990   /* Determine which type of EGADS model is attached to the DM */
5991   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
5992   if (!modelObj) { PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj)); }
5993 
5994   if (!modelObj) { PetscFunctionReturn(PETSC_SUCCESS); }
5995 
5996   // Look to see if DM has Container for Geometry Control Point Data
5997   PetscCall(PetscObjectQuery((PetscObject)dm, "Surface Gradient Hash Table", (PetscObject *)&cpSurfGradHashTableObj));
5998   PetscCall(PetscObjectQuery((PetscObject)dm, "Surface Gradient Matrix", (PetscObject *)cpSurfGrad));
5999   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Coordinate Data Length", (PetscObject *)&cpArraySizeObj));
6000   PetscCall(PetscObjectQuery((PetscObject)dm, "Surface Area Control Point Gradient", (PetscObject *)&gradSACPVec));
6001   PetscCall(PetscObjectQuery((PetscObject)dm, "Volume Control Point Gradient", (PetscObject *)&gradVolCPVec));
6002   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight Data Length", (PetscObject *)&wArraySizeObj));
6003   PetscCall(PetscObjectQuery((PetscObject)dm, "Surface Area Weights Gradient", (PetscObject *)&gradSAWVec));
6004   PetscCall(PetscObjectQuery((PetscObject)dm, "Volume Weights Gradient", (PetscObject *)&gradVolWVec));
6005 
6006   // Get attached EGADS model Control Point and Weights Hash Tables and Data Arrays (pointer)
6007   if (cpSurfGradHashTableObj) {
6008     PetscCall(PetscContainerGetPointer(cpSurfGradHashTableObj, (void **)&cpSurfGradHashTableTemp));
6009     *cpSurfGradHashTable = cpSurfGradHashTableTemp;
6010   }
6011 
6012   if (cpArraySizeObj) {
6013     PetscCall(PetscContainerGetPointer(cpArraySizeObj, (void **)&cpArraySizePtr));
6014     *cpArraySize = *cpArraySizePtr;
6015   }
6016 
6017   if (gradSACPVec) PetscCall(VecGetArrayWrite(gradSACPVec, gradSACP));
6018   if (gradVolCPVec) PetscCall(VecGetArrayWrite(gradVolCPVec, gradVolCP));
6019   if (gradSAWVec) PetscCall(VecGetArrayWrite(gradSAWVec, gradSAW));
6020   if (gradVolWVec) PetscCall(VecGetArrayWrite(gradVolWVec, gradVolW));
6021 
6022   if (wArraySizeObj) {
6023     PetscCall(PetscContainerGetPointer(wArraySizeObj, (void **)&wArraySizePtr));
6024     *wArraySize = *wArraySizePtr;
6025   }
6026   PetscFunctionReturn(PETSC_SUCCESS);
6027 }
6028 
6029 PetscErrorCode DMPlexRestoreGeomGradData(DM dm, PetscHMapI *cpSurfGradHashTable, Mat *cpSurfGrad, PetscInt *cpArraySize, PetscScalar **gradSACP, PetscScalar **gradVolCP, PetscInt *wArraySize, PetscScalar **gradSAW, PetscScalar **gradVolW)
6030 {
6031   Vec gradSACPVec, gradVolCPVec, gradSAWVec, gradVolWVec;
6032 
6033   PetscFunctionBegin;
6034   PetscCall(PetscObjectQuery((PetscObject)dm, "Surface Area Control Point Gradient", (PetscObject *)&gradSACPVec));
6035   PetscCall(PetscObjectQuery((PetscObject)dm, "Volume Control Point Gradient", (PetscObject *)&gradVolCPVec));
6036   PetscCall(PetscObjectQuery((PetscObject)dm, "Surface Area Weights Gradient", (PetscObject *)&gradSAWVec));
6037   PetscCall(PetscObjectQuery((PetscObject)dm, "Volume Weights Gradient", (PetscObject *)&gradVolWVec));
6038 
6039   if (gradSACPVec) PetscCall(VecRestoreArrayWrite(gradSACPVec, gradSACP));
6040   if (gradVolCPVec) PetscCall(VecRestoreArrayWrite(gradVolCPVec, gradVolCP));
6041   if (gradSAWVec) PetscCall(VecRestoreArrayWrite(gradSAWVec, gradSAW));
6042   if (gradVolWVec) PetscCall(VecRestoreArrayWrite(gradVolWVec, gradVolW));
6043   PetscFunctionReturn(PETSC_SUCCESS);
6044 }
6045 
6046 /*@C
6047   DMPlexGetGeomCntrlPntMaps - Gets arrays which maps Control Point IDs to their associated Geometry FACE, EDGE, and VERTEX.
6048 
6049   Not collective
6050 
6051   Input Parameter:
6052 . dm - The DMPlex object with an attached PetscContainer storing a CAD Geometry object
6053 
6054   Output Parameters:
6055 + numCntrlPnts            - Number of Control Points defining the Geometry attached to the DMPlex
6056 . cntrlPntFaceMap         - Array containing the FACE ID for the Control Point. Array index corresponds to Control Point ID.
6057 . cntrlPntWeightFaceMap   - Array containing the FACE ID for the Control Point Weight. Array index corresponds to Control Point ID.
6058 . cntrlPntEdgeMap         - Array containing the EDGE ID for the Control Point. Array index corresponds to Control Point ID.
6059 . cntrlPntWeightEdgeMap   - Array containing the EDGE ID for the Control Point Weight. Array index corresponds to Control Point ID.
6060 . cntrlPntVertexMap       - Array containing the VERTEX ID for the Control Point. Array index corresponds to Control Point ID.
6061 - cntrlPntWeightVertexMap - Array containing the VERTEX ID for the Control Point Weight. Array index corresponds to Control Point ID.
6062 
6063   Note:
6064   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.
6065 
6066   Level: intermediate
6067 
6068 .seealso: DMPlexGeomDataAndGrads
6069 @*/
6070 PetscErrorCode DMPlexGetGeomCntrlPntMaps(DM dm, PetscInt *numCntrlPnts, PetscInt **cntrlPntFaceMap, PetscInt **cntrlPntWeightFaceMap, PetscInt **cntrlPntEdgeMap, PetscInt **cntrlPntWeightEdgeMap, PetscInt **cntrlPntVertexMap, PetscInt **cntrlPntWeightVertexMap)
6071 {
6072   PetscFunctionBeginHot;
6073   #ifdef PETSC_HAVE_EGADS
6074   PetscContainer modelObj, numCntrlPntsObj, cntrlPntFaceMapObj, cntrlPntWeightFaceMapObj, cntrlPntEdgeMapObj, cntrlPntWeightEdgeMapObj, cntrlPntVertexMapObj, cntrlPntWeightVertexMapObj;
6075   PetscInt      *numCntrlPntsPtr, *cntrlPntFaceMapPtr, *cntrlPntWeightFaceMapPtr, *cntrlPntEdgeMapPtr, *cntrlPntWeightEdgeMapPtr, *cntrlPntVertexMapPtr, *cntrlPntWeightVertexMapPtr;
6076 
6077   /* Determine which type of EGADS model is attached to the DM */
6078   PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
6079   if (!modelObj) { PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSlite Model", (PetscObject *)&modelObj)); }
6080 
6081   if (!modelObj) { PetscFunctionReturn(PETSC_SUCCESS); }
6082 
6083   // Look to see if DM has Container for Geometry Control Point Data
6084   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight Data Length", (PetscObject *)&numCntrlPntsObj));
6085   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point - Face Map", (PetscObject *)&cntrlPntFaceMapObj));
6086   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight - Face Map", (PetscObject *)&cntrlPntWeightFaceMapObj));
6087   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point - Edge Map", (PetscObject *)&cntrlPntEdgeMapObj));
6088   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight - Edge Map", (PetscObject *)&cntrlPntWeightEdgeMapObj));
6089   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point - Vertex Map", (PetscObject *)&cntrlPntVertexMapObj));
6090   PetscCall(PetscObjectQuery((PetscObject)dm, "Control Point Weight - Vertex Map", (PetscObject *)&cntrlPntWeightVertexMapObj));
6091 
6092   // Get attached EGADS model Control Point and Weights Hash Tables and Data Arrays (pointer)
6093   if (numCntrlPntsObj) {
6094     PetscCall(PetscContainerGetPointer(numCntrlPntsObj, (void **)&numCntrlPntsPtr));
6095     *numCntrlPnts = *numCntrlPntsPtr;
6096   }
6097 
6098   if (cntrlPntFaceMapObj) {
6099     PetscCall(PetscContainerGetPointer(cntrlPntFaceMapObj, (void **)&cntrlPntFaceMapPtr));
6100     *cntrlPntFaceMap = cntrlPntFaceMapPtr;
6101   }
6102 
6103   if (cntrlPntWeightFaceMapObj) {
6104     PetscCall(PetscContainerGetPointer(cntrlPntWeightFaceMapObj, (void **)&cntrlPntWeightFaceMapPtr));
6105     *cntrlPntWeightFaceMap = cntrlPntWeightFaceMapPtr;
6106   }
6107 
6108   if (cntrlPntEdgeMapObj) {
6109     PetscCall(PetscContainerGetPointer(cntrlPntEdgeMapObj, (void **)&cntrlPntEdgeMapPtr));
6110     *cntrlPntEdgeMap = cntrlPntEdgeMapPtr;
6111   }
6112 
6113   if (cntrlPntWeightEdgeMapObj) {
6114     PetscCall(PetscContainerGetPointer(cntrlPntWeightEdgeMapObj, (void **)&cntrlPntWeightEdgeMapPtr));
6115     *cntrlPntWeightEdgeMap = cntrlPntWeightEdgeMapPtr;
6116   }
6117 
6118   if (cntrlPntVertexMapObj) {
6119     PetscCall(PetscContainerGetPointer(cntrlPntVertexMapObj, (void **)&cntrlPntVertexMapPtr));
6120     *cntrlPntVertexMap = cntrlPntVertexMapPtr;
6121   }
6122 
6123   if (cntrlPntWeightVertexMapObj) {
6124     PetscCall(PetscContainerGetPointer(cntrlPntWeightVertexMapObj, (void **)&cntrlPntWeightVertexMapPtr));
6125     *cntrlPntWeightVertexMap = cntrlPntWeightVertexMapPtr;
6126   }
6127 
6128   #endif
6129   PetscFunctionReturn(PETSC_SUCCESS);
6130 }
6131 
6132 #endif
6133