xref: /petsc/src/dm/impls/plex/plexgmsh.c (revision 3652e64ee7432af8673cbdda7b5a7f9c40e85382)
1 #define PETSCDM_DLL
2 #include <petsc/private/dmpleximpl.h>    /*I   "petscdmplex.h"   I*/
3 
4 /*@C
5   DMPlexCreateGmshFromFile - Create a DMPlex mesh from a Gmsh file
6 
7 + comm        - The MPI communicator
8 . filename    - Name of the Gmsh file
9 - interpolate - Create faces and edges in the mesh
10 
11   Output Parameter:
12 . dm  - The DM object representing the mesh
13 
14   Level: beginner
15 
16 .seealso: DMPlexCreateFromFile(), DMPlexCreateGmsh(), DMPlexCreate()
17 @*/
18 PetscErrorCode DMPlexCreateGmshFromFile(MPI_Comm comm, const char filename[], PetscBool interpolate, DM *dm)
19 {
20   PetscViewer     viewer, vheader;
21   PetscMPIInt     rank;
22   PetscViewerType vtype;
23   char            line[PETSC_MAX_PATH_LEN];
24   int             snum;
25   PetscBool       match;
26   int             fT;
27   PetscInt        fileType;
28   float           version;
29   PetscErrorCode  ierr;
30 
31   PetscFunctionBegin;
32   ierr = MPI_Comm_rank(comm, &rank);CHKERRQ(ierr);
33   /* Determine Gmsh file type (ASCII or binary) from file header */
34   ierr = PetscViewerCreate(comm, &vheader);CHKERRQ(ierr);
35   ierr = PetscViewerSetType(vheader, PETSCVIEWERASCII);CHKERRQ(ierr);
36   ierr = PetscViewerFileSetMode(vheader, FILE_MODE_READ);CHKERRQ(ierr);
37   ierr = PetscViewerFileSetName(vheader, filename);CHKERRQ(ierr);
38   if (!rank) {
39     /* Read only the first two lines of the Gmsh file */
40     ierr = PetscViewerRead(vheader, line, 1, NULL, PETSC_STRING);CHKERRQ(ierr);
41     ierr = PetscStrncmp(line, "$MeshFormat", PETSC_MAX_PATH_LEN, &match);CHKERRQ(ierr);
42     if (!match) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "File is not a valid Gmsh file");
43     ierr = PetscViewerRead(vheader, line, 2, NULL, PETSC_STRING);CHKERRQ(ierr);
44     snum = sscanf(line, "%f %d", &version, &fT);
45     fileType = (PetscInt) fT;
46     if (snum != 2) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse Gmsh file header: %s", line);
47     if (version < 2.0) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Gmsh file must be at least version 2.0");
48   }
49   ierr = MPI_Bcast(&fileType, 1, MPIU_INT, 0, comm);CHKERRQ(ierr);
50   /* Create appropriate viewer and build plex */
51   if (fileType == 0) vtype = PETSCVIEWERASCII;
52   else vtype = PETSCVIEWERBINARY;
53   ierr = PetscViewerCreate(comm, &viewer);CHKERRQ(ierr);
54   ierr = PetscViewerSetType(viewer, vtype);CHKERRQ(ierr);
55   ierr = PetscViewerFileSetMode(viewer, FILE_MODE_READ);CHKERRQ(ierr);
56   ierr = PetscViewerFileSetName(viewer, filename);CHKERRQ(ierr);
57   ierr = DMPlexCreateGmsh(comm, viewer, interpolate, dm);CHKERRQ(ierr);
58   ierr = PetscViewerDestroy(&viewer);CHKERRQ(ierr);
59   ierr = PetscViewerDestroy(&vheader);CHKERRQ(ierr);
60   PetscFunctionReturn(0);
61 }
62 
63 
64 /*@
65   DMPlexCreateGmsh - Create a DMPlex mesh from a Gmsh file viewer
66 
67   Collective on comm
68 
69   Input Parameters:
70 + comm  - The MPI communicator
71 . viewer - The Viewer associated with a Gmsh file
72 - interpolate - Create faces and edges in the mesh
73 
74   Output Parameter:
75 . dm  - The DM object representing the mesh
76 
77   Note: http://www.geuz.org/gmsh/doc/texinfo/#MSH-ASCII-file-format
78   and http://www.geuz.org/gmsh/doc/texinfo/#MSH-binary-file-format
79 
80   Level: beginner
81 
82 .keywords: mesh,Gmsh
83 .seealso: DMPLEX, DMCreate()
84 @*/
85 PetscErrorCode DMPlexCreateGmsh(MPI_Comm comm, PetscViewer viewer, PetscBool interpolate, DM *dm)
86 {
87   PetscViewerType vtype;
88   GmshElement     *gmsh_elem;
89   PetscSection    coordSection;
90   Vec             coordinates;
91   PetscScalar     *coords;
92   PetscReal       *coordsIn = NULL;
93   PetscInt        dim = 0, coordSize, c, v, d, r, cell;
94   int             i, numVertices = 0, numCells = 0, trueNumCells = 0, numRegions = 0, snum;
95   PetscMPIInt     num_proc, rank;
96   char            line[PETSC_MAX_PATH_LEN];
97   PetscBool       match, binary, bswap = PETSC_FALSE;
98   PetscErrorCode  ierr;
99 
100   PetscFunctionBegin;
101   ierr = MPI_Comm_rank(comm, &rank);CHKERRQ(ierr);
102   ierr = MPI_Comm_size(comm, &num_proc);CHKERRQ(ierr);
103   ierr = DMCreate(comm, dm);CHKERRQ(ierr);
104   ierr = DMSetType(*dm, DMPLEX);CHKERRQ(ierr);
105   ierr = PetscLogEventBegin(DMPLEX_CreateGmsh,*dm,0,0,0);CHKERRQ(ierr);
106   ierr = PetscViewerGetType(viewer, &vtype);CHKERRQ(ierr);
107   ierr = PetscStrcmp(vtype, PETSCVIEWERBINARY, &binary);CHKERRQ(ierr);
108   if (!rank || binary) {
109     PetscBool match;
110     int       fileType, dataSize;
111     float     version;
112 
113     /* Read header */
114     ierr = PetscViewerRead(viewer, line, 1, NULL, PETSC_STRING);CHKERRQ(ierr);
115     ierr = PetscStrncmp(line, "$MeshFormat", PETSC_MAX_PATH_LEN, &match);CHKERRQ(ierr);
116     if (!match) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "File is not a valid Gmsh file");
117     ierr = PetscViewerRead(viewer, line, 3, NULL, PETSC_STRING);CHKERRQ(ierr);
118     snum = sscanf(line, "%f %d %d", &version, &fileType, &dataSize);
119     if (snum != 3) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse Gmsh file header: %s", line);
120     if (version < 2.0) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Gmsh file must be at least version 2.0");
121     if (dataSize != sizeof(double)) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Data size %d is not valid for a Gmsh file", dataSize);
122     if (binary) {
123       int checkInt;
124       ierr = PetscViewerRead(viewer, &checkInt, 1, NULL, PETSC_ENUM);CHKERRQ(ierr);
125       if (checkInt != 1) {
126         ierr = PetscByteSwap(&checkInt, PETSC_ENUM, 1);CHKERRQ(ierr);
127         if (checkInt == 1) bswap = PETSC_TRUE;
128         else SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "File type %d is not a valid Gmsh binary file", fileType);
129       }
130     } else if (fileType) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "File type %d is not a valid Gmsh ASCII file", fileType);
131     ierr = PetscViewerRead(viewer, line, 1, NULL, PETSC_STRING);CHKERRQ(ierr);
132     ierr = PetscStrncmp(line, "$EndMeshFormat", PETSC_MAX_PATH_LEN, &match);CHKERRQ(ierr);
133     if (!match) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "File is not a valid Gmsh file");
134     /* OPTIONAL Read physical names */
135     ierr = PetscViewerRead(viewer, line, 1, NULL, PETSC_STRING);CHKERRQ(ierr);
136     ierr = PetscStrncmp(line, "$PhysicalNames", PETSC_MAX_PATH_LEN, &match);CHKERRQ(ierr);
137     if (match) {
138       ierr = PetscViewerRead(viewer, line, 1, NULL, PETSC_STRING);CHKERRQ(ierr);
139       snum = sscanf(line, "%d", &numRegions);
140       if (snum != 1) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "File is not a valid Gmsh file");
141       for (r = 0; r < numRegions; ++r) {ierr = PetscViewerRead(viewer, line, 3, NULL, PETSC_STRING);CHKERRQ(ierr);}
142       ierr = PetscViewerRead(viewer, line, 1, NULL, PETSC_STRING);CHKERRQ(ierr);
143       ierr = PetscStrncmp(line, "$EndPhysicalNames", PETSC_MAX_PATH_LEN, &match);CHKERRQ(ierr);
144       if (!match) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "File is not a valid Gmsh file");
145       /* Initial read for vertex section */
146       ierr = PetscViewerRead(viewer, line, 1, NULL, PETSC_STRING);CHKERRQ(ierr);
147     }
148     /* Read vertices */
149     ierr = PetscStrncmp(line, "$Nodes", PETSC_MAX_PATH_LEN, &match);CHKERRQ(ierr);
150     if (!match) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "File is not a valid Gmsh file");
151     ierr = PetscViewerRead(viewer, line, 1, NULL, PETSC_STRING);CHKERRQ(ierr);
152     snum = sscanf(line, "%d", &numVertices);
153     if (snum != 1) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "File is not a valid Gmsh file");
154     ierr = PetscMalloc1(numVertices*3, &coordsIn);CHKERRQ(ierr);
155     if (binary) {
156       size_t doubleSize, intSize;
157       PetscInt elementSize;
158       char *buffer;
159       PetscReal *baseptr;
160       ierr = PetscDataTypeGetSize(PETSC_ENUM, &intSize);CHKERRQ(ierr);
161       ierr = PetscDataTypeGetSize(PETSC_DOUBLE, &doubleSize);CHKERRQ(ierr);
162       elementSize = (intSize + 3*doubleSize);
163       ierr = PetscMalloc1(elementSize*numVertices, &buffer);CHKERRQ(ierr);
164       ierr = PetscViewerRead(viewer, buffer, elementSize*numVertices, NULL, PETSC_CHAR);CHKERRQ(ierr);
165       if (bswap) ierr = PetscByteSwap(buffer, PETSC_CHAR, elementSize*numVertices);CHKERRQ(ierr);
166       for (v = 0; v < numVertices; ++v) {
167         baseptr = ((PetscReal*)(buffer+v*elementSize+intSize));
168         coordsIn[v*3+0] = baseptr[0];
169         coordsIn[v*3+1] = baseptr[1];
170         coordsIn[v*3+2] = baseptr[2];
171       }
172       ierr = PetscFree(buffer);CHKERRQ(ierr);
173     } else {
174       for (v = 0; v < numVertices; ++v) {
175         ierr = PetscViewerRead(viewer, &i, 1, NULL, PETSC_ENUM);CHKERRQ(ierr);
176         ierr = PetscViewerRead(viewer, &(coordsIn[v*3]), 3, NULL, PETSC_REAL);CHKERRQ(ierr);
177         if (i != (int)v+1) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid node number %D should be %D", i, v+1);
178       }
179     }
180     ierr = PetscViewerRead(viewer, line, 1, NULL, PETSC_STRING);CHKERRQ(ierr);;
181     ierr = PetscStrncmp(line, "$EndNodes", PETSC_MAX_PATH_LEN, &match);CHKERRQ(ierr);
182     if (!match) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "File is not a valid Gmsh file");
183     /* Read cells */
184     ierr = PetscViewerRead(viewer, line, 1, NULL, PETSC_STRING);CHKERRQ(ierr);;
185     ierr = PetscStrncmp(line, "$Elements", PETSC_MAX_PATH_LEN, &match);CHKERRQ(ierr);
186     if (!match) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "File is not a valid Gmsh file");
187     ierr = PetscViewerRead(viewer, line, 1, NULL, PETSC_STRING);CHKERRQ(ierr);;
188     snum = sscanf(line, "%d", &numCells);
189     if (snum != 1) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "File is not a valid Gmsh file");
190   }
191 
192   if (!rank || binary) {
193     /* Gmsh elements can be of any dimension/co-dimension, so we need to traverse the
194        file contents multiple times to figure out the true number of cells and facets
195        in the given mesh. To make this more efficient we read the file contents only
196        once and store them in memory, while determining the true number of cells. */
197     ierr = DMPlexCreateGmsh_ReadElement(viewer, numCells, binary, bswap, &gmsh_elem);CHKERRQ(ierr);
198     for (trueNumCells=0, c = 0; c < numCells; ++c) {
199       if (gmsh_elem[c].dim > dim) {dim = gmsh_elem[c].dim; trueNumCells = 0;}
200       if (gmsh_elem[c].dim == dim) trueNumCells++;
201     }
202     ierr = PetscViewerRead(viewer, line, 1, NULL, PETSC_STRING);CHKERRQ(ierr);;
203     ierr = PetscStrncmp(line, "$EndElements", 12, &match);CHKERRQ(ierr);
204     if (!match) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "File is not a valid Gmsh file");
205   }
206   /* For binary we read on all ranks, but only build the plex on rank 0 */
207   if (binary && rank) {trueNumCells = 0; numVertices = 0;};
208   /* Allocate the cell-vertex mesh */
209   ierr = DMPlexSetChart(*dm, 0, trueNumCells+numVertices);CHKERRQ(ierr);
210   if (!rank) {
211     for (cell = 0, c = 0; c < numCells; ++c) {
212       if (gmsh_elem[c].dim == dim) {
213         ierr = DMPlexSetConeSize(*dm, cell, gmsh_elem[c].numNodes);CHKERRQ(ierr);
214         cell++;
215       }
216     }
217   }
218   ierr = DMSetUp(*dm);CHKERRQ(ierr);
219   /* Add cell-vertex connections */
220   if (!rank) {
221     PetscInt pcone[8], corner;
222     for (cell = 0, c = 0; c < numCells; ++c) {
223       if (gmsh_elem[c].dim == dim) {
224         for (corner = 0; corner < gmsh_elem[c].numNodes; ++corner) {
225           pcone[corner] = gmsh_elem[c].nodes[corner] + trueNumCells-1;
226         }
227         if (dim == 3) {
228           /* Tetrahedra are inverted */
229           if (gmsh_elem[c].numNodes == 4) {
230             PetscInt tmp = pcone[0];
231             pcone[0] = pcone[1];
232             pcone[1] = tmp;
233           }
234           /* Hexahedra are inverted */
235           if (gmsh_elem[c].numNodes == 8) {
236             PetscInt tmp = pcone[1];
237             pcone[1] = pcone[3];
238             pcone[3] = tmp;
239           }
240         }
241         ierr = DMPlexSetCone(*dm, cell, pcone);CHKERRQ(ierr);
242         cell++;
243       }
244     }
245   }
246   ierr = MPI_Bcast(&dim, 1, MPIU_INT, 0, comm);CHKERRQ(ierr);
247   ierr = DMSetDimension(*dm, dim);CHKERRQ(ierr);
248   ierr = DMPlexSymmetrize(*dm);CHKERRQ(ierr);
249   ierr = DMPlexStratify(*dm);CHKERRQ(ierr);
250   if (interpolate) {
251     DM idm = NULL;
252 
253     ierr = DMPlexInterpolate(*dm, &idm);CHKERRQ(ierr);
254     ierr = DMDestroy(dm);CHKERRQ(ierr);
255     *dm  = idm;
256   }
257 
258   if (!rank) {
259     /* Apply boundary IDs by finding the relevant facets with vertex joins */
260     PetscInt pcone[8], corner, vStart, vEnd;
261 
262     ierr = DMPlexGetDepthStratum(*dm, 0, &vStart, &vEnd);CHKERRQ(ierr);
263     for (c = 0; c < numCells; ++c) {
264       if (gmsh_elem[c].dim == dim-1) {
265         PetscInt joinSize;
266         const PetscInt *join;
267         for (corner = 0; corner < gmsh_elem[c].numNodes; ++corner) {
268           pcone[corner] = gmsh_elem[c].nodes[corner] + vStart - 1;
269         }
270         ierr = DMPlexGetFullJoin(*dm, gmsh_elem[c].numNodes, (const PetscInt *) pcone, &joinSize, &join);CHKERRQ(ierr);
271         if (joinSize != 1) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not determine Plex facet for element %d", gmsh_elem[c].id);
272         ierr = DMSetLabelValue(*dm, "Face Sets", join[0], gmsh_elem[c].tags[0]);CHKERRQ(ierr);
273         ierr = DMPlexRestoreJoin(*dm, gmsh_elem[c].numNodes, (const PetscInt *) pcone, &joinSize, &join);CHKERRQ(ierr);
274       }
275     }
276 
277     /* Create cell sets */
278     for (cell = 0, c = 0; c < numCells; ++c) {
279       if (gmsh_elem[c].dim == dim) {
280         if (gmsh_elem[c].numTags > 0) {
281           ierr = DMSetLabelValue(*dm, "Cell Sets", cell, gmsh_elem[c].tags[0]);CHKERRQ(ierr);
282           cell++;
283         }
284       }
285     }
286 
287     /* Create vertex sets */
288     for (c = 0; c < numCells; ++c) {
289       if (gmsh_elem[c].dim == 0) {
290         if (gmsh_elem[c].numTags > 0) {
291           ierr = DMSetLabelValue(*dm, "Vertex Sets", gmsh_elem[c].nodes[0] + vStart - 1, gmsh_elem[c].tags[0]);CHKERRQ(ierr);
292         }
293       }
294     }
295   }
296 
297   /* Read coordinates */
298   ierr = DMGetCoordinateSection(*dm, &coordSection);CHKERRQ(ierr);
299   ierr = PetscSectionSetNumFields(coordSection, 1);CHKERRQ(ierr);
300   ierr = PetscSectionSetFieldComponents(coordSection, 0, dim);CHKERRQ(ierr);
301   ierr = PetscSectionSetChart(coordSection, trueNumCells, trueNumCells + numVertices);CHKERRQ(ierr);
302   for (v = trueNumCells; v < trueNumCells+numVertices; ++v) {
303     ierr = PetscSectionSetDof(coordSection, v, dim);CHKERRQ(ierr);
304     ierr = PetscSectionSetFieldDof(coordSection, v, 0, dim);CHKERRQ(ierr);
305   }
306   ierr = PetscSectionSetUp(coordSection);CHKERRQ(ierr);
307   ierr = PetscSectionGetStorageSize(coordSection, &coordSize);CHKERRQ(ierr);
308   ierr = VecCreate(PETSC_COMM_SELF, &coordinates);CHKERRQ(ierr);
309   ierr = PetscObjectSetName((PetscObject) coordinates, "coordinates");CHKERRQ(ierr);
310   ierr = VecSetSizes(coordinates, coordSize, PETSC_DETERMINE);CHKERRQ(ierr);
311   ierr = VecSetBlockSize(coordinates, dim);CHKERRQ(ierr);
312   ierr = VecSetType(coordinates, VECSTANDARD);CHKERRQ(ierr);
313   ierr = VecGetArray(coordinates, &coords);CHKERRQ(ierr);
314   if (!rank) {
315     for (v = 0; v < numVertices; ++v) {
316       for (d = 0; d < dim; ++d) {
317         coords[v*dim+d] = coordsIn[v*3+d];
318       }
319     }
320   }
321   ierr = VecRestoreArray(coordinates, &coords);CHKERRQ(ierr);
322   ierr = PetscFree(coordsIn);CHKERRQ(ierr);
323   ierr = DMSetCoordinatesLocal(*dm, coordinates);CHKERRQ(ierr);
324   ierr = VecDestroy(&coordinates);CHKERRQ(ierr);
325   /* Clean up intermediate storage */
326   if (!rank || binary) ierr = PetscFree(gmsh_elem);CHKERRQ(ierr);
327   ierr = PetscLogEventEnd(DMPLEX_CreateGmsh,*dm,0,0,0);CHKERRQ(ierr);
328   PetscFunctionReturn(0);
329 }
330 
331 PetscErrorCode DMPlexCreateGmsh_ReadElement(PetscViewer viewer, PetscInt numCells, PetscBool binary, PetscBool byteSwap, GmshElement **gmsh_elems)
332 {
333   PetscInt       c, p;
334   GmshElement   *elements;
335   int            i, cellType, dim, numNodes, numElem, numTags;
336   int            ibuf[16];
337   PetscErrorCode ierr;
338 
339   PetscFunctionBegin;
340   ierr = PetscMalloc1(numCells, &elements);CHKERRQ(ierr);
341   for (c = 0; c < numCells;) {
342     ierr = PetscViewerRead(viewer, &ibuf, 3, NULL, PETSC_ENUM);CHKERRQ(ierr);
343     if (byteSwap) ierr = PetscByteSwap(&ibuf, PETSC_ENUM, 3);CHKERRQ(ierr);
344     if (binary) {
345       cellType = ibuf[0];
346       numElem = ibuf[1];
347       numTags = ibuf[2];
348     } else {
349       elements[c].id = ibuf[0];
350       cellType = ibuf[1];
351       numTags = ibuf[2];
352       numElem = 1;
353     }
354     switch (cellType) {
355     case 1: /* 2-node line */
356       dim = 1;
357       numNodes = 2;
358       break;
359     case 2: /* 3-node triangle */
360       dim = 2;
361       numNodes = 3;
362       break;
363     case 3: /* 4-node quadrangle */
364       dim = 2;
365       numNodes = 4;
366       break;
367     case 4: /* 4-node tetrahedron */
368       dim  = 3;
369       numNodes = 4;
370       break;
371     case 5: /* 8-node hexahedron */
372       dim = 3;
373       numNodes = 8;
374       break;
375     case 15: /* 1-node vertex */
376       dim = 0;
377       numNodes = 1;
378       break;
379     default:
380       SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unsupported Gmsh element type %d", cellType);
381     }
382     if (binary) {
383       const PetscInt nint = numNodes + numTags + 1;
384       for (i = 0; i < numElem; ++i, ++c) {
385         /* Loop over inner binary element block */
386         elements[c].dim = dim;
387         elements[c].numNodes = numNodes;
388         elements[c].numTags = numTags;
389 
390         ierr = PetscViewerRead(viewer, &ibuf, nint, NULL, PETSC_ENUM);CHKERRQ(ierr);
391         if (byteSwap) ierr = PetscByteSwap( &ibuf, PETSC_ENUM, nint);CHKERRQ(ierr);
392         elements[c].id = ibuf[0];
393         for (p = 0; p < numTags; p++) elements[c].tags[p] = ibuf[1 + p];
394         for (p = 0; p < numNodes; p++) elements[c].nodes[p] = ibuf[1 + numTags + p];
395       }
396     } else {
397       elements[c].dim = dim;
398       elements[c].numNodes = numNodes;
399       elements[c].numTags = numTags;
400       ierr = PetscViewerRead(viewer, elements[c].tags, elements[c].numTags, NULL, PETSC_ENUM);CHKERRQ(ierr);
401       ierr = PetscViewerRead(viewer, elements[c].nodes, elements[c].numNodes, NULL, PETSC_ENUM);CHKERRQ(ierr);
402       c++;
403     }
404   }
405   *gmsh_elems = elements;
406   PetscFunctionReturn(0);
407 }
408