1 /*******************************************************************/
2 /* XDMF */
3 /* eXtensible Data Model and Format */
4 /* */
5 /* Id : Id */
6 /* Date : $Date$ */
7 /* Version : $Revision$ */
8 /* */
9 /* Author: */
10 /* Kenneth Leiter */
11 /* kenneth.leiter@arl.army.mil */
12 /* US Army Research Laboratory */
13 /* Aberdeen Proving Ground, MD */
14 /* */
15 /* Copyright @ 2009 US Army Research Laboratory */
16 /* All Rights Reserved */
17 /* See Copyright.txt or http://www.arl.hpc.mil/ice for details */
18 /* */
19 /* This software is distributed WITHOUT ANY WARRANTY; without */
20 /* even the implied warranty of MERCHANTABILITY or FITNESS */
21 /* FOR A PARTICULAR PURPOSE. See the above copyright notice */
22 /* for more information. */
23 /* */
24 /*******************************************************************/
25
26 #include "XdmfExodusReader.h"
27 #include <exodusII.h>
28
29 //
30 // Construct XdmfExodusReader.
31 //
XdmfExodusReader()32 XdmfExodusReader::XdmfExodusReader()
33 {
34 return;
35 }
36
37 //
38 // Destroy XdmfExodusReader
39 //
~XdmfExodusReader()40 XdmfExodusReader::~XdmfExodusReader()
41 {
42 return;
43 }
44
45 //
46 // Translate from exodus ii to xdmf topologies
47 // this was taken directly from vtkExodusIIReader and modified to fit xdmf elements
48 //
DetermineXdmfCellType(char * exoElemType,int numPointsPerCell)49 XdmfInt32 XdmfExodusReader::DetermineXdmfCellType(char * exoElemType, int numPointsPerCell)
50 {
51 // Make exoType uppercase
52 std::string elemType = exoElemType;
53 std::transform(elemType.begin(), elemType.end(), elemType.begin(), toupper);
54
55 // Check for quadratic elements
56 if (elemType.substr(0,3) == "TRI" && numPointsPerCell == 6)
57 {
58 return XDMF_TRI_6;
59 }
60 else if (elemType.substr(0,3) == "SHE" && numPointsPerCell == 8)
61 {
62 return XDMF_QUAD_8;
63 }
64 else if (elemType.substr(0,3) == "SHE" && numPointsPerCell == 9)
65 {
66 // VTK_QUADRATIC_QUAD with 9 points
67 // Currently unsupported in Xdmf
68 return XDMF_QUAD_9;
69 }
70 else if (elemType.substr(0,3) == "TET" && numPointsPerCell == 10)
71 {
72 return XDMF_TET_10;
73 }
74 else if (elemType.substr(0,3) == "TET" && numPointsPerCell == 11)
75 {
76 // VTK_QUADRATIC_TETRA with 11 points
77 // Currently unsupported in Xdmf
78 return XDMF_NOTOPOLOGY;
79 }
80 else if (elemType.substr(0,3) == "WED" && numPointsPerCell == 15)
81 {
82 return XDMF_WEDGE_15;
83 }
84 else if (elemType.substr(0,3) == "HEX" && numPointsPerCell == 20)
85 {
86 return XDMF_HEX_20;
87 }
88 else if (elemType.substr(0,3) == "HEX" && numPointsPerCell == 21)
89 {
90 // VTK_QUADRATIC_HEXAHEDRON with 21 points
91 // Currently unsupported in Xdmf
92 return XDMF_NOTOPOLOGY;
93 }
94 else if (elemType.substr(0,3) == "HEX" && numPointsPerCell == 27)
95 {
96 return XDMF_HEX_27;
97 }
98 else if (elemType.substr(0,3) == "QUA" && numPointsPerCell == 8)
99 {
100 return XDMF_QUAD_8;
101 }
102 else if (elemType.substr(0,3) == "QUA" && numPointsPerCell == 9)
103 {
104 // VTK_BIQUADRATIC_QUAD;
105 // Currently unsupported in Xdmf
106 return XDMF_QUAD_9;
107 }
108 else if (elemType.substr(0,3) == "TRU" && numPointsPerCell == 3)
109 {
110 return XDMF_EDGE_3;
111 }
112 else if (elemType.substr(0,3) == "BEA" && numPointsPerCell == 3)
113 {
114 return XDMF_EDGE_3;
115 }
116 else if (elemType.substr(0,3) == "BAR" && numPointsPerCell == 3)
117 {
118 return XDMF_EDGE_3;
119 }
120 else if (elemType.substr(0,3) == "EDG" && numPointsPerCell == 3)
121 {
122 return XDMF_EDGE_3;
123 }
124 // Check for regular elements
125 else if (elemType.substr(0,3) == "CIR")
126 {
127 // VTK_VERTEX;
128 // Currently unsupported in Xdmf
129 return XDMF_NOTOPOLOGY;
130 }
131 else if (elemType.substr(0,3) == "SPH")
132 {
133 // VTK_VERTEX;
134 // Currently unsupported in Xdmf
135 return XDMF_NOTOPOLOGY;
136 }
137 else if (elemType.substr(0,3) == "BAR")
138 {
139 // VTK_LINE;
140 // Currently unsupported in Xdmf
141 return XDMF_NOTOPOLOGY;
142 }
143 else if (elemType.substr(0,3) == "TRU")
144 {
145 // VTK_LINE;
146 // Currently unsupported in Xdmf
147 return XDMF_NOTOPOLOGY;
148 }
149 else if (elemType.substr(0,3) == "BEA")
150 {
151 // VTK_LINE;
152 // Currently unsupported in Xdmf
153 return XDMF_NOTOPOLOGY;
154 }
155 else if (elemType.substr(0,3) == "EDG")
156 {
157 // VTK_LINE;
158 // Currently unsupported in Xdmf
159 return XDMF_NOTOPOLOGY;
160 }
161 else if (elemType.substr(0,3) == "TRI")
162 {
163 return XDMF_TRI;
164 }
165 else if (elemType.substr(0,3) == "QUA")
166 {
167 return XDMF_QUAD;
168 }
169 else if (elemType.substr(0,3) == "TET")
170 {
171 return XDMF_TET;
172 }
173 else if (elemType.substr(0,3) == "PYR")
174 {
175 return XDMF_PYRAMID;
176 }
177 else if (elemType.substr(0,3) == "WED")
178 {
179 return XDMF_WEDGE;
180 }
181 else if (elemType.substr(0,3) == "HEX")
182 {
183 return XDMF_HEX;
184 }
185 else if (elemType.substr(0,3) == "SHE" && numPointsPerCell == 3)
186 {
187 return XDMF_TRI;
188 }
189 else if (elemType.substr(0,3) == "SHE" && numPointsPerCell == 4)
190 {
191 return XDMF_QUAD;
192 }
193 else if (elemType.substr(0,8) == "STRAIGHT" && numPointsPerCell == 2)
194 {
195 // VTK_LINE;
196 // Currently unsupported in Xdmf
197 return XDMF_NOTOPOLOGY;
198 }
199 else if (elemType.substr(0,3) == "SUP")
200 {
201 return XDMF_POLYVERTEX;
202 }
203 //vtkErrorMacro("Unsupported element type: " << elemType.c_str());
204 return XDMF_NOTOPOLOGY;
205 }
206
207 //
208 // Read contents of the exodus file and fill in xdmf grids.
209 //
read(const char * fileName,XdmfElement * parentElement)210 XdmfGrid * XdmfExodusReader::read(const char * fileName, XdmfElement * parentElement)
211 {
212 XdmfGrid * grid = new XdmfGrid();
213 parentElement->Insert(grid);
214
215 // Read Exodus II file to XdmfGrid via Exodus II API
216
217 float version;
218 int CPU_word_size = sizeof(double);
219 int IO_word_size = 0; // Get from file
220 int exodusHandle = ex_open(fileName, EX_READ, &CPU_word_size, &IO_word_size, &version);
221
222 char * title = new char[MAX_LINE_LENGTH+1];
223 int num_dim, num_nodes, num_elem, num_elem_blk, num_node_sets, num_side_sets;
224 ex_get_init (exodusHandle, title, &num_dim, &num_nodes, &num_elem, &num_elem_blk, &num_node_sets, &num_side_sets);
225
226 /*
227 cout << "Title: " << title <<
228 "\nNum Dim: " << num_dim <<
229 "\nNum Nodes: " << num_nodes <<
230 "\nNum Elem: " << num_elem <<
231 "\nNum Elem Blk: " << num_elem_blk <<
232 "\nNum Node Sets: " << num_node_sets <<
233 "\nNum Side Sets: " << num_side_sets << endl;
234 */
235
236 // Read geometry values
237 double * x = new double[num_nodes];
238 double * y = new double[num_nodes];
239 double * z = new double[num_nodes];
240
241 ex_get_coord(exodusHandle, x, y, z);
242
243 XdmfGeometry * geom = grid->GetGeometry();
244 if(num_dim < 2 || num_dim > 3)
245 {
246 // Xdmf does not support geometries with less than 2 dimensions
247 std::cout << "Exodus File contains geometry of dimension " << num_dim << "which is unsupported by Xdmf" << std::endl;
248 return NULL;
249 }
250
251 // In the future we may want to do XDMF_GEOMETRY_X_Y_Z?
252 if(num_dim == 2)
253 {
254 geom->SetGeometryType(XDMF_GEOMETRY_XY);
255 }
256 else
257 {
258 geom->SetGeometryType(XDMF_GEOMETRY_XYZ);
259 }
260 geom->SetNumberOfPoints(num_nodes);
261 geom->SetDeleteOnGridDelete(true);
262
263 XdmfArray * points = geom->GetPoints();
264 points->SetNumberType(XDMF_FLOAT64_TYPE);
265 points->SetNumberOfElements(num_nodes * num_dim);
266 for (int j=0; j<num_nodes; j++)
267 {
268 points->SetValue((j * num_dim), x[j]);
269 points->SetValue((j * num_dim) + 1, y[j]);
270 if(num_dim == 3)
271 {
272 points->SetValue((j * num_dim) + 2, z[j]);
273 }
274 }
275 delete [] x;
276 delete [] y;
277 delete [] z;
278
279 int * blockIds = new int[num_elem_blk];
280 ex_get_elem_blk_ids(exodusHandle, blockIds);
281
282 int * numElemsInBlock = new int[num_elem_blk];
283 int * numNodesPerElemInBlock = new int[num_elem_blk];
284 int * numElemAttrInBlock = new int[num_elem_blk];
285 XdmfInt32 * topTypeInBlock = new XdmfInt32[num_elem_blk];
286 int totalNumElem = 0;
287 int totalConns = 0;
288 for (int j=0; j<num_elem_blk; j++)
289 {
290 char * elem_type = new char[MAX_STR_LENGTH+1];
291 int num_nodes_per_elem, num_elem_this_blk, num_attr;
292 ex_get_elem_block(exodusHandle, blockIds[j], elem_type, &num_elem_this_blk, &num_nodes_per_elem, &num_attr);
293
294 /*
295 cout << "Block Id: " << blockIds[j] <<
296 "\nElem Type: " << elem_type <<
297 "\nNum Elem in Blk: " << num_elem_this_blk <<
298 "\nNum Nodes per Elem: " << num_nodes_per_elem <<
299 "\nNum Attr: " << num_attr << endl;
300 */
301
302 numElemsInBlock[j] = num_elem_this_blk;
303 numNodesPerElemInBlock[j] = num_nodes_per_elem;
304 numElemAttrInBlock[j] = num_attr;
305 topTypeInBlock[j] = this->DetermineXdmfCellType(elem_type, num_nodes_per_elem);
306 totalNumElem = totalNumElem + num_elem_this_blk;
307 totalConns = totalConns + num_elem_this_blk * num_nodes_per_elem;
308 delete [] elem_type;
309 }
310
311 // Read connectivity from element blocks
312 // TODO: Make this work for mixed topologies?
313 XdmfInt32 topType;
314 int * conn = new int[totalConns];
315 int elemIndex = 0;
316 for (int j=0; j<num_elem_blk; ++j)
317 {
318 if (topTypeInBlock[j] != XDMF_NOTOPOLOGY)
319 {
320 topType = topTypeInBlock[j];
321 ex_get_elem_conn(exodusHandle, blockIds[j], &conn[elemIndex]);
322 elemIndex = elemIndex + numElemsInBlock[j] * numNodesPerElemInBlock[j];
323 }
324 }
325
326 // This is taken from VTK's vtkExodusIIReader and adapted to fit Xdmf element types, which have the same ordering as VTK.
327 if(topType == XDMF_HEX_20 || topType == XDMF_HEX_27)
328 {
329 int * ptr = conn;
330 int k;
331 int itmp[4];
332
333 // Exodus Node ordering does not match Xdmf, we must convert.
334 for (int i=0; i<totalNumElem; i++)
335 {
336 ptr += 12;
337
338 for ( k = 0; k < 4; ++k, ++ptr)
339 {
340 itmp[k] = *ptr;
341 *ptr = ptr[4];
342 }
343
344 for ( k = 0; k < 4; ++k, ++ptr )
345 {
346 *ptr = itmp[k];
347 }
348
349 if(topType == XDMF_HEX_27)
350 {
351 for ( k = 0; k < 4; ++k, ++ptr )
352 {
353 itmp[k] = *ptr;
354 *ptr = ptr[3];
355 }
356 *(ptr++) = itmp[1];
357 *(ptr++) = itmp[2];
358 *(ptr++) = itmp[0];
359 }
360 }
361 }
362 else if (topType == XDMF_WEDGE_15 || topType == XDMF_WEDGE_18)
363 {
364 int * ptr = conn;
365 int k;
366 int itmp[3];
367
368 // Exodus Node ordering does not match Xdmf, we must convert.
369 for (int i=0; i<totalNumElem; i++)
370 {
371 ptr += 9;
372
373 for (k = 0; k < 3; ++k, ++ptr)
374 {
375 itmp[k] = *ptr;
376 *ptr = ptr[3];
377 }
378
379 for (k = 0; k < 3; ++k, ++ptr)
380 {
381 *ptr = itmp[k];
382 }
383
384 if(topType == XDMF_WEDGE_18)
385 {
386 itmp[0] = *(ptr);
387 itmp[1] = *(ptr+1);
388 itmp[2] = *(ptr+2);
389 *(ptr++) = itmp[1];
390 *(ptr++) = itmp[2];
391 *(ptr++) = itmp[0];
392 }
393 }
394 }
395
396 XdmfTopology * topology = grid->GetTopology();
397 topology->SetTopologyType(topType);
398 topology->SetNumberOfElements(totalNumElem);
399 topology->SetDeleteOnGridDelete(true);
400
401 XdmfArray * connections = topology->GetConnectivity();
402 connections->SetNumberType(XDMF_INT32_TYPE);
403 connections->SetNumberOfElements(totalConns);
404 connections->SetValues(0, conn, totalConns, 1, 1);
405 // Subtract all node ids by 1 since exodus indices start at 1
406 *connections - 1;
407 delete [] conn;
408
409 // Get nodal map to global ids and write global ids to xdmf
410 int * node_map = new int[num_nodes];
411 ex_get_node_num_map(exodusHandle, node_map);
412
413 XdmfAttribute * globalIds = new XdmfAttribute();
414 globalIds->SetName("GlobalNodeId");
415 globalIds->SetAttributeType(XDMF_ATTRIBUTE_TYPE_SCALAR);
416 globalIds->SetAttributeCenter(XDMF_ATTRIBUTE_CENTER_NODE);
417 globalIds->SetDeleteOnGridDelete(true);
418
419 XdmfArray * globalNodeIdVals = globalIds->GetValues();
420 globalNodeIdVals->SetNumberType(XDMF_INT32_TYPE);
421 globalNodeIdVals->SetNumberOfElements(num_nodes);
422 globalNodeIdVals->SetValues(0, node_map, num_nodes, 1, 1);
423 // Subtract all node ids by 1 since exodus indices start at 1
424 *globalNodeIdVals - 1;
425 grid->Insert(globalIds);
426 delete [] node_map;
427
428 // Read node sets
429 int * nodeSetIds = new int[num_node_sets];
430 ex_get_node_set_ids(exodusHandle, nodeSetIds);
431
432 char * node_set_names[num_node_sets];
433 for (int j=0; j<num_node_sets; j++)
434 {
435 node_set_names[j] = new char[MAX_STR_LENGTH+1];
436 }
437 ex_get_names(exodusHandle, EX_NODE_SET, node_set_names);
438
439 for (int j=0; j<num_node_sets; j++)
440 {
441 int num_nodes_in_set, num_df_in_set;
442 ex_get_node_set_param(exodusHandle, nodeSetIds[j], &num_nodes_in_set, &num_df_in_set);
443
444 /*
445 cout << "Node Set Id: " << nodeSetIds[j] <<
446 "\nNode Set Name: " << node_set_names[j] <<
447 "\nNum Nodes in Set: "<< num_nodes_in_set <<
448 "\nNum Distrub Factors: " << num_df_in_set << endl;
449 */
450
451 if (num_nodes_in_set > 0)
452 {
453 int * node_set_node_list = new int[num_nodes_in_set];
454 ex_get_node_set(exodusHandle, nodeSetIds[j], node_set_node_list);
455
456 XdmfSet * set = new XdmfSet();
457 set->SetName(node_set_names[j]);
458 set->SetSetType(XDMF_SET_TYPE_NODE);
459 set->SetSize(num_nodes_in_set);
460 set->SetDeleteOnGridDelete(true);
461
462 XdmfArray * ids = set->GetIds();
463 ids->SetNumberType(XDMF_INT32_TYPE);
464 ids->SetNumberOfElements(num_nodes_in_set);
465 ids->SetValues(0, node_set_node_list, num_nodes_in_set, 1, 1);
466 // Subtract all node ids by 1 since exodus indices start at 1
467 *ids - 1;
468 grid->Insert(set);
469
470 /*
471 if(num_df_in_set > 0)
472 {
473 double * node_set_distribution_factors = new double[num_df_in_set];
474 ex_get_node_set_dist_fact(exodusHandle, nodeSetIds[j], node_set_distribution_factors);
475
476 XdmfAttribute * attr = new XdmfAttribute();
477 attr->SetName("SetAttribute");
478 attr->SetAttributeType(XDMF_ATTRIBUTE_TYPE_SCALAR);
479 attr->SetAttributeCenter(XDMF_ATTRIBUTE_CENTER_NODE);
480 attr->SetDeleteOnGridDelete(true);
481
482 XdmfArray * attrVals = attr->GetValues();
483 attrVals->SetNumberType(XDMF_FLOAT32_TYPE);
484 attrVals->SetNumberOfElements(num_df_in_set);
485 attrVals->SetValues(0, node_set_distribution_factors, num_df_in_set, 1, 1);
486 set->Insert(attr);
487 delete [] node_set_distribution_factors;
488 }
489 */
490
491 delete [] node_set_node_list;
492 }
493 delete [] node_set_names[j];
494 }
495 delete [] nodeSetIds;
496
497 // Read result variables (attributes)
498 int num_global_vars, num_nodal_vars, num_elem_vars;
499 ex_get_var_param(exodusHandle, "g", &num_global_vars);
500 ex_get_var_param(exodusHandle, "n", &num_nodal_vars);
501 ex_get_var_param(exodusHandle, "e", &num_elem_vars);
502
503 /*
504 cout << "Num Global Vars: " << num_global_vars <<
505 "\nNum Nodal Vars: " << num_nodal_vars <<
506 "\nNum Elem Vars: " << num_elem_vars << endl;
507 */
508
509 char * global_var_names[num_global_vars];
510 char * nodal_var_names[num_nodal_vars];
511 char * elem_var_names[num_elem_vars];
512 for (int j=0; j<num_global_vars; j++)
513 {
514 global_var_names[j] = new char[MAX_STR_LENGTH+1];
515 }
516 for (int j=0; j<num_nodal_vars; j++)
517 {
518 nodal_var_names[j] = new char[MAX_STR_LENGTH+1];
519 }
520 for (int j=0; j<num_elem_vars; j++)
521 {
522 elem_var_names[j] = new char[MAX_STR_LENGTH+1];
523 }
524 ex_get_var_names(exodusHandle, "g", num_global_vars, global_var_names);
525 ex_get_var_names(exodusHandle, "n", num_nodal_vars, nodal_var_names);
526 ex_get_var_names(exodusHandle, "e", num_elem_vars, elem_var_names);
527
528 /*
529 cout << "Global Vars Names: " << endl;
530 for (int j=0; j<num_global_vars; j++)
531 {
532 cout << global_var_names[j] << endl;
533 }
534 cout << "Nodal Vars Names: " << endl;
535 for (int j=0; j<num_nodal_vars; j++)
536 {
537 cout << nodal_var_names[j] << endl;
538 }
539 cout << "Elem Vars Names: " << endl;
540 for (int j=0; j<num_elem_vars; j++)
541 {
542 cout << elem_var_names[j] << endl;
543 }
544 */
545
546 // Get variable data
547 // TODO: do this for all timesteps?
548
549 // Global variable data
550 double * global_var_vals = new double[num_global_vars];
551 ex_get_glob_vars(exodusHandle, 1, num_global_vars, &global_var_vals);
552 for (int j=0; j<num_global_vars; j++)
553 {
554 // Write global attribute to xdmf
555 XdmfAttribute * attr = new XdmfAttribute();
556 attr->SetName(global_var_names[j]);
557 attr->SetAttributeType(XDMF_ATTRIBUTE_TYPE_SCALAR);
558 attr->SetAttributeCenter(XDMF_ATTRIBUTE_CENTER_GRID);
559 attr->SetDeleteOnGridDelete(true);
560
561 XdmfArray * attrVals = attr->GetValues();
562 attrVals->SetNumberType(XDMF_FLOAT64_TYPE);
563 attrVals->SetNumberOfElements(1);
564 attrVals->SetValues(0, &global_var_vals[j], 1, 1, 1);
565 grid->Insert(attr);
566 delete [] global_var_names[j];
567 }
568 delete [] global_var_vals;
569
570 // Nodal variable data
571 for (int j=0; j<num_nodal_vars; j++)
572 {
573 // The strcmp with "GlobalNodeId" is meant to prevent errors from occuring when a nodal variable is named GlobalNodeId.
574 // A GlobalNodeId attribute was added before when adding the nodal map which means that this attribute should be ignored...
575 // This will probably only occur when doing repeated conversions --- i.e. Xdmf to Exodus to Xdmf to Exodus...
576 if (strcmp(nodal_var_names[j], "GlobalNodeId") != 0)
577 {
578 double * nodal_var_vals = new double[num_nodes];
579 ex_get_nodal_var(exodusHandle, 1, j+1, num_nodes, nodal_var_vals);
580
581 // Write nodal attribute to xdmf
582 XdmfAttribute * attr = new XdmfAttribute();
583 attr->SetName(nodal_var_names[j]);
584 attr->SetAttributeType(XDMF_ATTRIBUTE_TYPE_SCALAR);
585 attr->SetAttributeCenter(XDMF_ATTRIBUTE_CENTER_NODE);
586 attr->SetDeleteOnGridDelete(true);
587
588 XdmfArray * attrVals = attr->GetValues();
589 attrVals->SetNumberType(XDMF_FLOAT64_TYPE);
590 attrVals->SetNumberOfElements(num_nodes);
591 attrVals->SetValues(0, nodal_var_vals, num_nodes, 1, 1);
592 grid->Insert(attr);
593 delete [] nodal_var_vals;
594 delete [] nodal_var_names[j];
595 }
596 }
597
598 // Element variable data
599 for (int j=0; j<num_elem_vars; j++)
600 {
601 double * elem_var_vals = new double[totalNumElem];
602 elemIndex = 0;
603 for (int k=0; k<num_elem_blk; k++)
604 {
605 ex_get_elem_var(exodusHandle, 1, j+1, blockIds[k], numElemsInBlock[j], &elem_var_vals[elemIndex]);
606 elemIndex = elemIndex + numElemsInBlock[j];
607 }
608
609 // Write element attribute to xdmf
610 XdmfAttribute * attr = new XdmfAttribute();
611 attr->SetName(elem_var_names[j]);
612 attr->SetAttributeType(XDMF_ATTRIBUTE_TYPE_SCALAR);
613 attr->SetAttributeCenter(XDMF_ATTRIBUTE_CENTER_CELL);
614 attr->SetDeleteOnGridDelete(true);
615
616 XdmfArray * attrVals = attr->GetValues();
617 attrVals->SetNumberType(XDMF_FLOAT64_TYPE);
618 attrVals->SetNumberOfElements(totalNumElem);
619 attrVals->SetValues(0, elem_var_vals, totalNumElem, 1, 1);
620 grid->Insert(attr);
621 delete [] elem_var_vals;
622 delete [] elem_var_names[j];
623 }
624
625 ex_close(exodusHandle);
626 delete [] title;
627 delete [] blockIds;
628 delete [] numElemsInBlock;
629 delete [] numNodesPerElemInBlock;
630 delete [] numElemAttrInBlock;
631 delete [] topTypeInBlock;
632 return grid;
633 }
634