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