1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkAMREnzoReaderInternal.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 #include "vtkAMREnzoReaderInternal.h"
16 
17 #define H5_USE_16_API
18 #include "vtk_hdf5.h"        // for the HDF5 library
19 
20 #include "vtksys/SystemTools.hxx"
21 #include "vtkCellData.h"
22 #include "vtkPointData.h"
23 #include "vtkPolyData.h"
24 #include "vtkFloatArray.h"
25 #include "vtkIntArray.h"
26 #include "vtkDoubleArray.h"
27 #include "vtkUnsignedIntArray.h"
28 #include "vtkShortArray.h"
29 #include "vtkUnsignedShortArray.h"
30 #include "vtkLongArray.h"
31 #include "vtkLongLongArray.h"
32 #include "vtkDataArray.h"
33 #include "vtkDataSet.h"
34 
35 // ----------------------------------------------------------------------------
36 //                       Functions for Parsing File Names
37 // ----------------------------------------------------------------------------
38 
39 
GetEnzoMajorFileName(const char * path)40 static std::string GetEnzoMajorFileName( const char * path )
41 {
42   return(vtksys::SystemTools::GetFilenameName( std::string( path ) ) );
43 }
44 
45 // ----------------------------------------------------------------------------
46 //                       Class vtkEnzoReaderBlock (begin)
47 // ----------------------------------------------------------------------------
48 //------------------------------------------------------------------------------
Init()49 void vtkEnzoReaderBlock::Init()
50 {
51   this->BlockFileName    = "";
52   this->ParticleFileName = "";
53 
54   this->Index    = -1;
55   this->Level    = -1;
56   this->ParentId = -1;
57   this->ChildrenIds.clear();
58   this->NumberOfParticles  = 0;
59   this->NumberOfDimensions = 0;
60 
61   this->MinParentWiseIds[0] =
62   this->MinParentWiseIds[1] =
63   this->MinParentWiseIds[2] =
64   this->MaxParentWiseIds[0] =
65   this->MaxParentWiseIds[1] =
66   this->MaxParentWiseIds[2] = -1;
67 
68   this->MinLevelBasedIds[0] =
69   this->MinLevelBasedIds[1] =
70   this->MinLevelBasedIds[2] =
71   this->MaxLevelBasedIds[0] =
72   this->MaxLevelBasedIds[1] =
73   this->MaxLevelBasedIds[2] = -1;
74 
75   this->BlockCellDimensions[0] =
76   this->BlockCellDimensions[1] =
77   this->BlockCellDimensions[2] =
78   this->BlockNodeDimensions[0] =
79   this->BlockNodeDimensions[1] =
80   this->BlockNodeDimensions[2] = 0;
81 
82   this->MinBounds[0] =
83   this->MinBounds[1] =
84   this->MinBounds[2] = VTK_DOUBLE_MAX;
85   this->MaxBounds[0] =
86   this->MaxBounds[1] =
87   this->MaxBounds[2] =-VTK_DOUBLE_MAX;
88 
89   this->SubdivisionRatio[0] =
90   this->SubdivisionRatio[1] =
91   this->SubdivisionRatio[2] = 1.0;
92 }
93 
94 //------------------------------------------------------------------------------
DeepCopy(const vtkEnzoReaderBlock * other)95 void vtkEnzoReaderBlock::DeepCopy(const vtkEnzoReaderBlock *other)
96 {
97   this->BlockFileName    = other->BlockFileName;
98   this->ParticleFileName = other->ParticleFileName;
99 
100   this->Index    = other->Index;
101   this->Level    = other->Level;
102   this->ParentId = other->ParentId;
103   this->ChildrenIds = other->ChildrenIds;
104   this->NumberOfParticles  = other->NumberOfParticles;
105   this->NumberOfDimensions = other->NumberOfDimensions;
106 
107   this->MinParentWiseIds[0] = other->MinParentWiseIds[0];
108   this->MinParentWiseIds[1] = other->MinParentWiseIds[1];
109   this->MinParentWiseIds[2] = other->MinParentWiseIds[2];
110   this->MaxParentWiseIds[0] = other->MaxParentWiseIds[0];
111   this->MaxParentWiseIds[1] = other->MaxParentWiseIds[1];
112   this->MaxParentWiseIds[2] = other->MaxParentWiseIds[2];
113 
114   this->MinLevelBasedIds[0] = other->MinLevelBasedIds[0];
115   this->MinLevelBasedIds[1] = other->MinLevelBasedIds[1];
116   this->MinLevelBasedIds[2] = other->MinLevelBasedIds[2];
117   this->MaxLevelBasedIds[0] = other->MaxLevelBasedIds[0];
118   this->MaxLevelBasedIds[1] = other->MaxLevelBasedIds[1];
119   this->MaxLevelBasedIds[2] = other->MaxLevelBasedIds[2];
120 
121   this->BlockCellDimensions[0] = other->BlockCellDimensions[0];
122   this->BlockCellDimensions[1] = other->BlockCellDimensions[1];
123   this->BlockCellDimensions[2] = other->BlockCellDimensions[2];
124   this->BlockNodeDimensions[0] = other->BlockNodeDimensions[0];
125   this->BlockNodeDimensions[1] = other->BlockNodeDimensions[1];
126   this->BlockNodeDimensions[2] = other->BlockNodeDimensions[2];
127 
128   this->MinBounds[0] = other->MinBounds[0];
129   this->MinBounds[1] = other->MinBounds[1];
130   this->MinBounds[2] = other->MinBounds[2];
131   this->MaxBounds[0] = other->MaxBounds[0];
132   this->MaxBounds[1] = other->MaxBounds[1];
133   this->MaxBounds[2] = other->MaxBounds[2];
134 
135   this->SubdivisionRatio[0] = other->SubdivisionRatio[0];
136   this->SubdivisionRatio[1] = other->SubdivisionRatio[1];
137   this->SubdivisionRatio[2] = other->SubdivisionRatio[2];
138 }
139 
140 //------------------------------------------------------------------------------
141 // get the bounding (cell) Ids of this block in terms of its parent block's
142 // sub-division resolution (indexing is limited to the scope of the parent)
GetParentWiseIds(std::vector<vtkEnzoReaderBlock> & blocks)143 void vtkEnzoReaderBlock::GetParentWiseIds
144   (  std::vector< vtkEnzoReaderBlock > & blocks  )
145 {
146   if ( this->ParentId != 0 )
147   {
148     // the parent is not the root and then we need to determine the offset
149     // (in terms of the number of parent divisions / cells) of the current
150     // block's beginning / ending position relative to the parent block's
151     // beginning position
152     vtkEnzoReaderBlock & parent = blocks[ this->ParentId ];
153     this->MinParentWiseIds[0]   = static_cast < int >
154           (  0.5 + parent.BlockCellDimensions[0]
155                  * ( this->MinBounds[0]  - parent.MinBounds[0] )
156                  / ( parent.MaxBounds[0] - parent.MinBounds[0] )  );
157     this->MaxParentWiseIds[0]   = static_cast < int >
158           (  0.5 +  parent.BlockCellDimensions[0]
159                  * ( this->MaxBounds[0]  - parent.MinBounds[0] )
160                  / ( parent.MaxBounds[0] - parent.MinBounds[0] )  );
161 
162     this->MinParentWiseIds[1]   = static_cast < int >
163           (  0.5 + parent.BlockCellDimensions[1]
164                  * ( this->MinBounds[1]  - parent.MinBounds[1] )
165                  / ( parent.MaxBounds[1] - parent.MinBounds[1] )  );
166     this->MaxParentWiseIds[1]   = static_cast < int >
167           (  0.5 + parent.BlockCellDimensions[1]
168                  * ( this->MaxBounds[1]  - parent.MinBounds[1] )
169                  / ( parent.MaxBounds[1] - parent.MinBounds[1] )  );
170 
171     if ( this->NumberOfDimensions == 3 )
172     {
173       this->MinParentWiseIds[2] = static_cast < int >
174           (  0.5 + parent.BlockCellDimensions[2]
175                  * ( this->MinBounds[2]  - parent.MinBounds[2] )
176                  / ( parent.MaxBounds[2] - parent.MinBounds[2] )  );
177       this->MaxParentWiseIds[2] = static_cast < int >
178           (  0.5 + parent.BlockCellDimensions[2]
179                  * ( this->MaxBounds[2]  - parent.MinBounds[2] )
180                  / ( parent.MaxBounds[2] - parent.MinBounds[2] )  );
181     }
182     else
183     {
184       this->MinParentWiseIds[2] = 0;
185       this->MaxParentWiseIds[2] = 0;
186     }
187 
188     // the ratio for mapping two parent-wise Ids to 0 and
189     // this->BlockCellDimension[i],
190     // respectively, while the same region is covered
191     this->SubdivisionRatio[0] = static_cast < double >
192           ( this->BlockCellDimensions[0] ) /
193           ( this->MaxParentWiseIds[0] - this->MinParentWiseIds[0] );
194     this->SubdivisionRatio[1] = static_cast < double >
195           ( this->BlockCellDimensions[1] ) /
196           ( this->MaxParentWiseIds[1] - this->MinParentWiseIds[1] );
197 
198     if ( this->NumberOfDimensions == 3 )
199     {
200       this->SubdivisionRatio[2] = static_cast < double >
201           ( this->BlockCellDimensions[2] ) /
202           ( this->MaxParentWiseIds[2] - this->MinParentWiseIds[2] );
203     }
204     else
205     {
206       this->SubdivisionRatio[2] = 1.0;
207     }
208   }
209   else
210   {
211     // Now that the parent is the root, it can not provide cell-dimensions
212     // information (BlockCellDimensions[0 .. 2]) directly, as the above does.
213     // Fortunately we can obtain it according to the spatial ratio of the
214     // child block (the current one) to the parent (root) and the child block's
215     // cell-dimensions information. This derivation is based on the definition
216     // of 'level' that all children blocks at the same level (e.g., the current
217     // block and its siblings) share the same sub-division ratio relative to
218     // their parent (the root herein).
219     vtkEnzoReaderBlock & block0 = blocks[0];
220 
221     double xRatio = ( this->MaxBounds[0]  - this->MinBounds[0]  ) /
222                     ( block0.MaxBounds[0] - block0.MinBounds[0] );
223     this->MinParentWiseIds[0] = static_cast < int >
224           (  0.5 + ( this->BlockCellDimensions[0] / xRatio ) // parent's dim
225                  * ( this->MinBounds[0]  - block0.MinBounds[0] )
226                  / ( block0.MaxBounds[0] - block0.MinBounds[0] )
227           );
228     this->MaxParentWiseIds[0] = static_cast < int >
229           (  0.5 + ( this->BlockCellDimensions[0] / xRatio )
230                  * ( this->MaxBounds[0]  - block0.MinBounds[0] )
231                  / ( block0.MaxBounds[0] - block0.MinBounds[0] )
232           );
233 
234     double yRatio = ( this->MaxBounds[1]  - this->MinBounds[1]  ) /
235                     ( block0.MaxBounds[1] - block0.MinBounds[1] );
236     this->MinParentWiseIds[1] = static_cast < int >
237           (  0.5 + ( this->BlockCellDimensions[1] / yRatio )
238                  * ( this->MinBounds[1]  - block0.MinBounds[1] )
239                  / ( block0.MaxBounds[1] - block0.MinBounds[1] )
240           );
241     this->MaxParentWiseIds[1] = static_cast < int >
242           (  0.5 + ( this->BlockCellDimensions[1] / yRatio )
243                  * ( this->MaxBounds[1]  - block0.MinBounds[1] )
244                  / ( block0.MaxBounds[1] - block0.MinBounds[1] )
245           );
246 
247     if ( this->NumberOfDimensions == 3 )
248     {
249       double zRatio = ( this->MaxBounds[2]  - this->MinBounds[2]  ) /
250                       ( block0.MaxBounds[2] - block0.MinBounds[2] );
251       this->MinParentWiseIds[2] = static_cast < int >
252           (  0.5 + ( this->BlockCellDimensions[2] / zRatio )
253                  * ( this->MinBounds[2]  - block0.MinBounds[2] )
254                  / ( block0.MaxBounds[2] - block0.MinBounds[2] )
255           );
256       this->MaxParentWiseIds[2] = static_cast < int >
257           (  0.5 + ( this->BlockCellDimensions[2] / zRatio )
258                  * ( this->MaxBounds[2]  - block0.MinBounds[2] )
259                  / ( block0.MaxBounds[2] - block0.MinBounds[2] )
260           );
261     }
262     else
263     {
264       this->MinParentWiseIds[2] = 0;
265       this->MaxParentWiseIds[2] = 0;
266     }
267 
268     this->SubdivisionRatio[0] = 1.0;
269     this->SubdivisionRatio[1] = 1.0;
270     this->SubdivisionRatio[2] = 1.0;
271   }
272 }
273 
274 //------------------------------------------------------------------------------
GetLevelBasedIds(std::vector<vtkEnzoReaderBlock> & blocks)275 void vtkEnzoReaderBlock::GetLevelBasedIds
276   (  std::vector< vtkEnzoReaderBlock > & blocks  )
277 {
278   // note that this function is invoked from the root in a top-down manner
279   // and the parent-wise Ids have been determined in advance
280 
281   if ( this->ParentId != 0 )
282   {
283     // the parent is not the root and therefore we need to exploit the level-
284     // based Ids of the parent, of which the shifted version is multiplied by
285     // the refinement ratio
286 
287     vtkEnzoReaderBlock & parent = blocks[ this->ParentId ];
288     this->MinLevelBasedIds[0] = static_cast < int >
289                                 (  ( parent.MinLevelBasedIds[0]
290                                      + this->MinParentWiseIds[0]
291                                    ) * this->SubdivisionRatio[0]
292                                 );
293 
294     this->MinLevelBasedIds[1] = static_cast < int >
295                                 (  ( parent.MinLevelBasedIds[1]
296                                      + this->MinParentWiseIds[1]
297                                    ) * this->SubdivisionRatio[1]
298                                 );
299     this->MinLevelBasedIds[2] = static_cast < int >
300                                 (  ( parent.MinLevelBasedIds[2]
301                                      + this->MinParentWiseIds[2]
302                                    ) * this->SubdivisionRatio[2]
303                                 );
304 
305     this->MaxLevelBasedIds[0] = static_cast < int >
306                                 (  ( parent.MinLevelBasedIds[0]
307                                      + this->MaxParentWiseIds[0]
308                                    ) * this->SubdivisionRatio[0]
309                                 );
310     this->MaxLevelBasedIds[1] = static_cast < int >
311                                 (  ( parent.MinLevelBasedIds[1]
312                                      + this->MaxParentWiseIds[1]
313                                    ) * this->SubdivisionRatio[1]
314                                 );
315     this->MaxLevelBasedIds[2] = static_cast < int >
316                                 (  ( parent.MinLevelBasedIds[2]
317                                      + this->MaxParentWiseIds[2]
318                                    ) * this->SubdivisionRatio[2]
319                                 );
320   }
321   else
322   {
323     // now that the parent is the root, the parent-wise Ids
324     // are just the level-based Ids
325     this->MinLevelBasedIds[0] = this->MinParentWiseIds[0];
326     this->MinLevelBasedIds[1] = this->MinParentWiseIds[1];
327     this->MinLevelBasedIds[2] = this->MinParentWiseIds[2];
328 
329     this->MaxLevelBasedIds[0] = this->MaxParentWiseIds[0];
330     this->MaxLevelBasedIds[1] = this->MaxParentWiseIds[1];
331     this->MaxLevelBasedIds[2] = this->MaxParentWiseIds[2];
332   }
333 }
334 //------------------------------------------------------------------------------
335 // ----------------------------------------------------------------------------
336 //                       Class vtkEnzoReaderBlock ( end )
337 // ----------------------------------------------------------------------------
338 
339 // ----------------------------------------------------------------------------
340 //                     Class  vtkEnzoReaderInternal (begin)
341 // ----------------------------------------------------------------------------
342 
vtkEnzoReaderInternal()343 vtkEnzoReaderInternal::vtkEnzoReaderInternal()
344 {
345   this->Init();
346 }
347 
348 //------------------------------------------------------------------------------
~vtkEnzoReaderInternal()349 vtkEnzoReaderInternal::~vtkEnzoReaderInternal()
350 {
351   this->ReleaseDataArray();
352   this->Init();
353   this->FileName = nullptr;
354 }
355 
356 //------------------------------------------------------------------------------
Init()357 void vtkEnzoReaderInternal::Init()
358 {
359   this->DataTime   = 0.0;
360   this->FileName   = nullptr;
361   //this->TheReader  = nullptr;
362   this->DataArray  = nullptr;
363   this->CycleIndex = 0;
364 
365   this->ReferenceBlock = 0;
366   this->NumberOfBlocks = 0;
367   this->NumberOfLevels = 0;
368   this->NumberOfDimensions  = 0;
369   this->NumberOfMultiBlocks = 0;
370 
371   this->DirectoryName = "";
372   this->MajorFileName = "";
373   this->BoundaryFileName  = "";
374   this->HierarchyFileName = "";
375 
376   this->Blocks.clear();
377   this->BlockAttributeNames.clear();
378   this->ParticleAttributeNames.clear();
379   this->TracerParticleAttributeNames.clear();
380 }
381 
382 //------------------------------------------------------------------------------
ReleaseDataArray()383 void vtkEnzoReaderInternal::ReleaseDataArray()
384 {
385   if( this->DataArray )
386   {
387     this->DataArray->Delete();
388     this->DataArray = nullptr;
389   }
390 }
391 
392 //------------------------------------------------------------------------------
GetBlockAttribute(const char * attribute,int blockIdx,vtkDataSet * pDataSet)393 int vtkEnzoReaderInternal::GetBlockAttribute(
394     const char *attribute, int blockIdx, vtkDataSet *pDataSet )
395 {
396 
397   // this function must be called by GetBlock( ... )
398   this->ReadMetaData();
399 
400   if ( attribute == nullptr || blockIdx < 0  ||
401        pDataSet == nullptr || blockIdx >= this->NumberOfBlocks )
402   {
403     return 0;
404   }
405 
406   // try obtaining the attribute and attaching it to the grid as a cell data
407   // NOTE: the 'equal' comparison below is MUST because in some cases (such
408   // as cosmological datasets) not all rectilinear blocks contain an assumably
409   // common block attribute. This is the case where such a block attribute
410   // may result from a particles-to-cells interpolation process. Thus those
411   // blocks with particles (e.g., the reference one with the fewest cells)
412   // contain it as a block attribute, whereas others without particles just
413   // do not contain this block attribute.
414   int   succeeded = 0;
415   if (  this->LoadAttribute( attribute, blockIdx ) &&
416         ( pDataSet->GetNumberOfCells() ==
417           this->DataArray->GetNumberOfTuples() )
418      )
419   {
420     succeeded = 1;
421     pDataSet->GetCellData()->AddArray( this->DataArray );
422     this->ReleaseDataArray();
423   }
424 
425   return succeeded;
426 }
427 
428 //------------------------------------------------------------------------------
LoadAttribute(const char * attribute,int blockIdx)429 int vtkEnzoReaderInternal::LoadAttribute( const char *attribute, int blockIdx )
430 {
431   // TODO: implement this
432   // called by GetBlockAttribute( ... ) or GetParticlesAttribute()
433   this->ReadMetaData();
434 
435   if ( attribute == nullptr || blockIdx < 0  ||
436        blockIdx >= this->NumberOfBlocks )
437   {
438     return 0;
439   }
440 
441   // this->Internal->Blocks includes a pseudo block --- the root as block #0
442   blockIdx ++;
443 
444   // currently only the HDF5 file format is supported
445   std::string blckFile = this->Blocks[ blockIdx ].BlockFileName;
446   hid_t  fileIndx = H5Fopen( blckFile.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT );
447   if( fileIndx < 0 )
448   {
449     return 0;
450   }
451 
452   // retrieve the contents of the root directory to look for a group
453   // corresponding to the target block, if available, open that group
454 
455   int     blckIndx;
456   char    blckName[65];
457   int     objIndex;
458   hsize_t numbObjs;
459   hid_t   rootIndx = H5Gopen( fileIndx, "/" );
460   H5Gget_num_objs( rootIndx, &numbObjs );
461   for ( objIndex=0; objIndex < static_cast < int >( numbObjs ); objIndex ++  )
462   {
463     int type = H5Gget_objtype_by_idx( rootIndx, objIndex );
464     if ( type == H5G_GROUP )
465     {
466       H5Gget_objname_by_idx( rootIndx, objIndex, blckName, 64 );
467       if ( (  sscanf( blckName, "Grid%d", &blckIndx )  ==  1  )  &&
468            (  (blckIndx  ==  blockIdx) || (blckIndx == blockIdx+1) ) )
469       {
470         // located the target block
471         rootIndx = H5Gopen( rootIndx, blckName );
472         break;
473       }
474     }
475   } // END for all objects
476 
477   // disable error messages while looking for the attribute (if any) name
478   // and enable error messages when it is done
479   void       * pContext = nullptr;
480   H5E_auto_t   erorFunc;
481   H5Eget_auto( &erorFunc, &pContext );
482   H5Eset_auto( nullptr, nullptr );
483 
484   hid_t        attrIndx = H5Dopen( rootIndx, attribute );
485 
486   H5Eset_auto( erorFunc, pContext );
487   pContext = nullptr;
488 
489   // check if the data attribute exists
490   if ( attrIndx < 0 )
491   {
492     vtkGenericWarningMacro(
493      "Attribute (" << attribute << ") data does not exist in file "
494      << blckFile.c_str() );
495     H5Gclose( rootIndx );
496     H5Fclose( fileIndx );
497     return 0;
498   }
499 
500   // get cell dimensions and the valid number
501   hsize_t cellDims[3];
502   hid_t   spaceIdx = H5Dget_space( attrIndx );
503   H5Sget_simple_extent_dims( spaceIdx, cellDims, nullptr );
504   hsize_t numbDims = H5Sget_simple_extent_ndims( spaceIdx );
505 
506   // number of attribute tuples = number of cells (or particles)
507   int numTupls = 0;
508   switch( numbDims )
509   {
510     case 1:
511       numTupls = cellDims[0];
512       break;
513     case 2:
514       numTupls = cellDims[0] * cellDims[1];
515       break;
516     case 3:
517       numTupls = cellDims[0] * cellDims[1] * cellDims[2];
518       break;
519     default:
520       H5Gclose( spaceIdx );
521       H5Fclose( attrIndx );
522       H5Gclose( rootIndx );
523       H5Fclose( fileIndx );
524       return 0;
525   }
526 
527   // determine the data type, load the values, and, if necessary, convert
528   // the raw data to double type
529   // DOUBLE / FLOAT  / INT  / UINT  / CHAR  / UCHAR
530   // SHORT  / USHORT / LONG / ULONG / LLONG / ULLONG / LDOUBLE
531 
532   this->ReleaseDataArray(); // data array maintained by internal
533 
534   hid_t tRawType = H5Dget_type( attrIndx );
535   hid_t dataType = H5Tget_native_type( tRawType, H5T_DIR_ASCEND );
536   if (  H5Tequal( dataType, H5T_NATIVE_FLOAT )  )
537   {
538     this->DataArray = vtkFloatArray::New();
539     this->DataArray->SetNumberOfTuples( numTupls );
540     float  * arrayPtr = static_cast < float * >
541      (vtkArrayDownCast<vtkFloatArray>( this->DataArray )->GetPointer( 0 )  );
542     H5Dread( attrIndx, dataType, H5S_ALL, H5S_ALL, H5P_DEFAULT, arrayPtr );
543     arrayPtr = nullptr;
544   }
545   else if (  H5Tequal( dataType, H5T_NATIVE_DOUBLE )  )
546   {
547     this->DataArray = vtkDoubleArray::New();
548     this->DataArray->SetNumberOfTuples( numTupls );
549     double  * arrayPtr = static_cast < double * >
550      (vtkArrayDownCast<vtkDoubleArray>( this->DataArray )->GetPointer( 0 )  );
551     H5Dread( attrIndx, dataType, H5S_ALL, H5S_ALL, H5P_DEFAULT, arrayPtr );
552     arrayPtr = nullptr;
553   }
554   else if (  H5Tequal( dataType, H5T_NATIVE_INT )  )
555   {
556     this->DataArray = vtkIntArray::New();
557     this->DataArray->SetNumberOfTuples( numTupls );
558     int  * arrayPtr = static_cast < int * >
559      (vtkArrayDownCast<vtkIntArray>( this->DataArray )->GetPointer( 0 )  );
560     H5Dread( attrIndx, dataType, H5S_ALL, H5S_ALL, H5P_DEFAULT, arrayPtr );
561     arrayPtr = nullptr;
562   }
563   else if (  H5Tequal( dataType, H5T_NATIVE_UINT )  )
564   {
565     this->DataArray = vtkUnsignedIntArray::New();
566     this->DataArray->SetNumberOfTuples( numTupls );
567     unsigned int  * arrayPtr = static_cast < unsigned int * >
568      (vtkArrayDownCast<vtkUnsignedIntArray>( this->DataArray )->GetPointer( 0 )  );
569     H5Dread( attrIndx, dataType, H5S_ALL, H5S_ALL, H5P_DEFAULT, arrayPtr );
570     arrayPtr = nullptr;
571   }
572   else if (  H5Tequal( dataType, H5T_NATIVE_SHORT )  )
573   {
574     this->DataArray = vtkShortArray::New();
575     this->DataArray->SetNumberOfTuples( numTupls );
576     short  * arrayPtr = static_cast < short * >
577      (vtkArrayDownCast<vtkShortArray>( this->DataArray )->GetPointer( 0 )  );
578     H5Dread( attrIndx, dataType, H5S_ALL, H5S_ALL, H5P_DEFAULT, arrayPtr );
579     arrayPtr = nullptr;
580   }
581   else if (  H5Tequal( dataType, H5T_NATIVE_USHORT )  )
582   {
583     this->DataArray = vtkUnsignedShortArray::New();
584     this->DataArray->SetNumberOfTuples( numTupls );
585     unsigned short  * arrayPtr = static_cast < unsigned short * >
586      (vtkArrayDownCast<vtkUnsignedShortArray>( this->DataArray )->GetPointer( 0 ));
587     H5Dread( attrIndx, dataType, H5S_ALL, H5S_ALL, H5P_DEFAULT, arrayPtr );
588     arrayPtr = nullptr;
589   }
590   else if (  H5Tequal( dataType, H5T_NATIVE_UCHAR )  )
591   {
592 
593     this->DataArray = vtkUnsignedCharArray::New();
594     this->DataArray->SetNumberOfTuples( numTupls );
595     unsigned char  * arrayPtr = static_cast < unsigned char * >
596      (vtkArrayDownCast<vtkUnsignedCharArray>( this->DataArray )->GetPointer( 0 )  );
597     H5Dread( attrIndx, dataType, H5S_ALL, H5S_ALL, H5P_DEFAULT, arrayPtr );
598     arrayPtr = nullptr;
599   }
600   else if (  H5Tequal( dataType, H5T_NATIVE_LONG )  )
601   {
602     this->DataArray = vtkLongArray::New();
603     this->DataArray->SetNumberOfTuples( numTupls );
604     long  * arrayPtr = static_cast < long * >
605      (vtkArrayDownCast<vtkLongArray>( this->DataArray )->GetPointer( 0 )  );
606     H5Dread( attrIndx, dataType, H5S_ALL, H5S_ALL, H5P_DEFAULT, arrayPtr );
607     arrayPtr = nullptr;
608   }
609   else if (  H5Tequal( dataType, H5T_NATIVE_LLONG )  )
610   {
611     this->DataArray = vtkLongLongArray::New();
612     this->DataArray->SetNumberOfTuples( numTupls );
613     long long  * arrayPtr = static_cast < long long * >
614      ( vtkArrayDownCast<vtkLongLongArray>( this->DataArray )->GetPointer( 0 )  );
615     H5Dread( attrIndx, dataType, H5S_ALL, H5S_ALL, H5P_DEFAULT, arrayPtr );
616     arrayPtr = nullptr;
617   }
618   else
619   {
620 //        vtkErrorMacro( "Unknown HDF5 data type --- it is not FLOAT, "       <<
621 //                       "DOUBLE, INT, UNSIGNED INT, SHORT, UNSIGNED SHORT, " <<
622 //                       "UNSIGNED CHAR, LONG, or LONG LONG." << endl );
623     H5Tclose( dataType );
624     H5Tclose( tRawType );
625     H5Tclose( spaceIdx );
626     H5Dclose( attrIndx );
627     H5Gclose( rootIndx );
628     H5Fclose( fileIndx );
629     return 0;
630   }
631 
632 
633   // do not forget to provide a name for the array
634   this->DataArray->SetName( attribute );
635 
636 // These close statements cause a crash!
637 //  H5Tclose( dataType );
638 //  H5Tclose( tRawType );
639 //  H5Tclose( spaceIdx );
640 //  H5Dclose( attrIndx );
641 //  H5Gclose( rootIndx );
642 //  H5Fclose( fileIndx );
643   return 1;
644 }
645 
646 //------------------------------------------------------------------------------
647 // ----------------------------------------------------------------------------
648 // parse the hierarchy file to create block structures, including the bounding
649 // box, cell dimensions, grid / node dimensions, number of particles, level Id,
650 // block file name, and particle file name of each block
ReadBlockStructures()651 void vtkEnzoReaderInternal::ReadBlockStructures()
652 {
653   ifstream stream( this->HierarchyFileName.c_str() );
654   if ( !stream )
655   {
656     vtkGenericWarningMacro( "Invalid hierarchy file name: " <<
657                             this->HierarchyFileName.c_str() << endl );
658     return;
659   }
660 
661   // init the root block, addressing only 4 four fields
662   vtkEnzoReaderBlock  block0;
663   block0.Index    = 0;
664   block0.Level    =-1;
665   block0.ParentId =-1;
666   block0.NumberOfDimensions  = this->NumberOfDimensions;
667   this->Blocks.push_back( block0 );
668 
669   int     levlId = 0;
670   int     parent = 0;
671   std::string   theStr;
672 
673   while ( stream )
674   {
675     while ( stream &&
676             theStr != "Grid" &&
677             theStr != "Time" &&
678             theStr != "Pointer:"
679           )
680     {
681       stream >> theStr;
682     }
683 
684     // block information
685     if ( theStr == "Grid" )
686     {
687       vtkEnzoReaderBlock tmpBlk;
688       tmpBlk.NumberOfDimensions = this->NumberOfDimensions;
689 
690       stream >> theStr; // '='
691       stream >> tmpBlk.Index;
692 
693       // the starting and ending (cell --- not node) Ids of the block
694       int     minIds[3];
695       int     maxIds[3];
696       while ( theStr != "GridStartIndex" )
697       {
698         stream >> theStr;
699       }
700       stream >> theStr; // '='
701 
702       if ( this->NumberOfDimensions == 3 )
703       {
704         stream >> minIds[0] >> minIds[1] >> minIds[2];
705       }
706       else
707       {
708         stream >> minIds[0] >> minIds[1];
709       }
710 
711       while ( theStr != "GridEndIndex" )
712       {
713         stream >> theStr;
714       }
715       stream >> theStr; // '='
716 
717       if ( this->NumberOfDimensions == 3 )
718       {
719         stream >> maxIds[0] >> maxIds[1] >> maxIds[2];
720       }
721       else
722       {
723         stream >> maxIds[0] >> maxIds[1];
724       }
725 
726       // the cell dimensions of the block
727       tmpBlk.BlockCellDimensions[0] = maxIds[0] - minIds[0] + 1;
728       tmpBlk.BlockCellDimensions[1] = maxIds[1] - minIds[1] + 1;
729       if ( this->NumberOfDimensions == 3 )
730       {
731         tmpBlk.BlockCellDimensions[2] = maxIds[2] - minIds[2] + 1;
732       }
733       else
734       {
735         tmpBlk.BlockCellDimensions[2] = 1;
736       }
737 
738       // the grid (node --- not means the block) dimensions of the block
739       tmpBlk.BlockNodeDimensions[0] = tmpBlk.BlockCellDimensions[0] + 1;
740       tmpBlk.BlockNodeDimensions[1] = tmpBlk.BlockCellDimensions[1] + 1;
741       if ( this->NumberOfDimensions == 3 )
742       {
743         tmpBlk.BlockNodeDimensions[2] = tmpBlk.BlockCellDimensions[2] + 1;
744       }
745       else
746       {
747         tmpBlk.BlockNodeDimensions[2] = 1;
748       }
749 
750       // the min bounding box of the block
751       while ( theStr != "GridLeftEdge" )
752       {
753         stream >> theStr;
754       }
755       stream >> theStr; // '='
756 
757       if ( this->NumberOfDimensions == 3 )
758       {
759         stream >> tmpBlk.MinBounds[0]
760                >> tmpBlk.MinBounds[1] >> tmpBlk.MinBounds[2];
761       }
762       else
763       {
764         tmpBlk.MinBounds[2] = 0;
765         stream >> tmpBlk.MinBounds[0] >> tmpBlk.MinBounds[1];
766       }
767 
768       // the max bounding box of the block
769       while ( theStr != "GridRightEdge" )
770       {
771         stream >> theStr;
772       }
773       stream >> theStr; // '='
774 
775       if ( this->NumberOfDimensions == 3 )
776       {
777         stream >> tmpBlk.MaxBounds[0]
778                >> tmpBlk.MaxBounds[1] >> tmpBlk.MaxBounds[2];
779       }
780       else
781       {
782         tmpBlk.MaxBounds[2] = 0;
783         stream >> tmpBlk.MaxBounds[0] >> tmpBlk.MaxBounds[1];
784       }
785 
786       // obtain the block file name (szName includes the full path)
787       std::string    szName;
788       while ( theStr != "BaryonFileName" )
789       {
790         stream >> theStr;
791       }
792       stream >> theStr; // '='
793       stream >> szName;
794 
795 //      std::cout << "szname: " << szName.c_str() << std::endl;
796 //      std::cout.flush();
797       tmpBlk.BlockFileName =
798           this->DirectoryName + "/" + GetEnzoMajorFileName(szName.c_str());
799 
800       // obtain the particle file name (szName includes the full path)
801       while ( theStr != "NumberOfParticles" )
802       {
803         stream >> theStr;
804       }
805       stream >> theStr; // '='
806       stream >> tmpBlk.NumberOfParticles;
807 
808       if ( tmpBlk.NumberOfParticles > 0 )
809       {
810         while ( theStr != "ParticleFileName" )
811         {
812           stream >> theStr;
813         }
814         stream >> theStr; // '='
815         stream >> szName;
816         tmpBlk.ParticleFileName =
817             this->DirectoryName + "/" +GetEnzoMajorFileName(szName.c_str());
818       }
819 
820       tmpBlk.Level    = levlId;
821       tmpBlk.ParentId = parent;
822 
823       if (  static_cast < int > ( this->Blocks.size() )  !=  tmpBlk.Index  )
824       {
825         vtkGenericWarningMacro( "The blocks in the hierarchy file " <<
826                                 this->HierarchyFileName.c_str()     <<
827                                 " are currently expected to be "    <<
828                                 " listed in order."                 << endl );
829         return;
830       }
831 
832       this->Blocks.push_back( tmpBlk );
833       this->Blocks[parent].ChildrenIds.push_back( tmpBlk.Index );
834       this->NumberOfBlocks = static_cast < int > ( this->Blocks.size() ) - 1;
835     }
836     else if ( theStr == "Pointer:" )
837     {
838       theStr = "";
839       int    tmpInt;
840       char   tmpChr;
841       while (  ( tmpChr = stream.get() )  !=  '['  ) ;
842       while (  ( tmpChr = stream.get() )  !=  ']'  ) theStr += tmpChr;
843 
844       int    blkIdx = atoi( theStr.c_str() );
845       stream.get(); // -
846       stream.get(); // >
847       stream >> theStr;
848       if ( theStr == "NextGridNextLevel" )
849       {
850         stream >> theStr; // '='
851         stream >> tmpInt;
852         if ( tmpInt != 0 )
853         {
854           levlId = this->Blocks[blkIdx].Level + 1;
855           this->NumberOfLevels = ( levlId+1 > this->NumberOfLevels )
856                                ? ( levlId+1 ) : this->NumberOfLevels;
857           parent = blkIdx;
858         }
859       }
860       else // theStr == "NextGridThisLevel"
861       {
862         stream >> theStr; // '='
863         stream >> tmpInt;
864       }
865     }
866     else if ( theStr == "Time" )
867     {
868       stream >> theStr; // '='
869       stream >> this->DataTime;
870     }
871 
872     stream >> theStr;
873   }
874 
875   if( this->NumberOfLevels==0 && this->NumberOfBlocks > 0 )
876   {
877     // unigrid dataset, change the number of levels to 1
878     this->NumberOfLevels = 1;
879   }
880 
881   stream.close();
882 }
883 
884 //------------------------------------------------------------------------------
885 // ----------------------------------------------------------------------------
886 // obtain the general information of the dataset (number of dimensions)
ReadGeneralParameters()887 void vtkEnzoReaderInternal::ReadGeneralParameters()
888 {
889   ifstream stream( this->MajorFileName.c_str() );
890   if ( !stream )
891   {
892     vtkGenericWarningMacro( "Invalid parameter file " <<
893                             this->MajorFileName.c_str() << endl );
894     return;
895   }
896 
897   std::string tmpStr;
898   while ( stream )
899   {
900     stream >> tmpStr;
901 
902     if ( tmpStr == "InitialCycleNumber" )
903     {
904       stream >> tmpStr; // '='
905       stream >> this->CycleIndex;
906     }
907     else if ( tmpStr == "InitialTime" )
908     {
909       stream >> tmpStr; // '='
910       stream >> this->DataTime;
911     }
912     else if ( tmpStr == "TopGridRank" )
913     {
914       stream >> tmpStr; // '='
915       stream >> this->NumberOfDimensions;
916     }
917   }
918 
919   stream.close();
920 }
921 
922 //------------------------------------------------------------------------------
923 // ----------------------------------------------------------------------------
924 // get the bounding box of the root block based on those of its descendants
DetermineRootBoundingBox()925 void vtkEnzoReaderInternal::DetermineRootBoundingBox()
926 {
927   vtkEnzoReaderBlock & block0 = this->Blocks[0];
928 
929   // now loop over all level zero grids
930   for ( int blkIdx = 1; blkIdx <= this->NumberOfBlocks &&
931                         this->Blocks[blkIdx].ParentId == 0; blkIdx ++ )
932   for ( int dimIdx = 0; dimIdx <  this->NumberOfDimensions; dimIdx ++ )
933   {
934     block0.MinBounds[dimIdx] =
935     ( this->Blocks[ blkIdx ].MinBounds[ dimIdx ] < block0.MinBounds[ dimIdx ] )
936     ? this->Blocks[ blkIdx ].MinBounds[ dimIdx ] : block0.MinBounds[ dimIdx ];
937 
938     block0.MaxBounds[dimIdx] =
939     ( this->Blocks[ blkIdx ].MaxBounds[ dimIdx ] > block0.MaxBounds[ dimIdx ] )
940     ? this->Blocks[ blkIdx ].MaxBounds[ dimIdx ] : block0.MaxBounds[ dimIdx ];
941   }
942 }
943 
944 //------------------------------------------------------------------------------
945 // ----------------------------------------------------------------------------
946 // perform an initial collection of attribute names (for block and particles)
GetAttributeNames()947 void vtkEnzoReaderInternal::GetAttributeNames()
948 {
949   int   wasFound = 0;       // any block with particles was found?
950   int   blkIndex = 0;       // index of the block with fewest cells
951                             // (either with or without particles)
952   int   numCells = INT_MAX; // number of cells of a block
953   int   numbBlks = static_cast < int > ( this->Blocks.size() );
954 
955   for ( int i = 1; i < numbBlks; i ++ )
956   {
957     vtkEnzoReaderBlock & tmpBlock = this->Blocks[i];
958     if (  wasFound && ( tmpBlock.NumberOfParticles <= 0 )  )
959     {
960       continue;
961     }
962 
963     int  tempNumb = tmpBlock.BlockCellDimensions[0] *
964                     tmpBlock.BlockCellDimensions[1] *
965                     tmpBlock.BlockCellDimensions[2];
966 
967     if (  (  tempNumb  < numCells  ) ||
968           ( !wasFound && tmpBlock.NumberOfParticles > 0 )
969        )
970     {
971       if (  !wasFound ||
972            ( tmpBlock.NumberOfParticles > 0 )
973          )
974       {
975         numCells = tempNumb;
976         blkIndex = tmpBlock.Index;
977         wasFound = ( tmpBlock.NumberOfParticles > 0 ) ? 1 : 0;
978       }
979     }
980   }
981   this->ReferenceBlock = blkIndex;
982 
983 
984   // open the block file
985   std::string   blckFile = this->Blocks[ blkIndex ].BlockFileName;
986   hid_t   fileIndx = H5Fopen( blckFile.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT );
987 
988   if ( fileIndx < 0 )
989   {
990     vtkGenericWarningMacro(
991        "Failed to open HDF5 grid file " << blckFile.c_str() );
992     return;
993   }
994 
995   // retrieve the contents of the root directory to look for a group
996   // corresponding to the specified block name (the one with the fewest
997   // cells --- either with or without particles) and, if available, open
998   // that group
999 
1000   int     objIndex;
1001   hsize_t numbObjs;
1002   hid_t   rootIndx = H5Gopen( fileIndx, "/" );
1003   H5Gget_num_objs( rootIndx, &numbObjs );
1004 
1005   for (  objIndex = 0;  objIndex < static_cast < int > ( numbObjs );
1006          objIndex ++  )
1007   {
1008     if (  H5Gget_objtype_by_idx( rootIndx, objIndex )  ==  H5G_GROUP  )
1009     {
1010       int   blckIndx;
1011       char  blckName[65];
1012       H5Gget_objname_by_idx( rootIndx, objIndex, blckName, 64 );
1013 
1014       if (  sscanf( blckName, "Grid%d", &blckIndx ) == 1 &&
1015             ( blckIndx == blkIndex ) // does this block have the fewest cells?
1016          )
1017       {
1018         rootIndx = H5Gopen( rootIndx, blckName ); // located the target block
1019         break;
1020       }
1021     }
1022   }
1023 
1024 
1025   // in case of entering a sub-directory, obtain the number of objects here
1026   // and proceed with the parsing work (now rootIndx points to the group of
1027   // of target block)
1028   H5Gget_num_objs( rootIndx, &numbObjs );
1029 
1030   for (  objIndex = 0;  objIndex < static_cast < int > ( numbObjs );
1031          objIndex ++  )
1032   {
1033     if (  H5Gget_objtype_by_idx( rootIndx, objIndex ) == H5G_DATASET  )
1034     {
1035       char  tempName[65];
1036       H5Gget_objname_by_idx( rootIndx, objIndex, tempName, 64 );
1037 
1038       // NOTE: to do the same diligence as HDF4 here, we should
1039       // really H5Dopen, H5Dget_space, H5Sget_simple_extent_ndims
1040       // and make sure it is a 3D (or 2D?) object before assuming
1041       // it is a mesh variable.  For now, assume away!
1042       if (   (  strlen( tempName )  >  8  ) &&
1043              (  strncmp( tempName, "particle", 8 )  ==  0  )
1044          )
1045       {
1046         // it's a particle variable and skip over coordinate arrays
1047         if (  strncmp( tempName, "particle_position_", 18 ) != 0  )
1048         {
1049           this->ParticleAttributeNames.push_back( tempName );
1050         }
1051       }
1052       else if (   (  strlen( tempName )  >  16  ) &&
1053                   (  strncmp( tempName, "tracer_particles", 16 )  ==  0  )
1054               )
1055       {
1056         // it's a tracer_particle variable and skip over coordinate arrays
1057         if (  strncmp( tempName, "tracer_particle_position_", 25 ) != 0  )
1058         {
1059           this->TracerParticleAttributeNames.push_back( tempName );
1060         }
1061       }
1062       else
1063       {
1064         this->BlockAttributeNames.push_back( tempName );
1065       }
1066     }
1067   }
1068 
1069   H5Gclose( rootIndx );
1070   H5Fclose( fileIndx );
1071 }
1072 
1073 // ----------------------------------------------------------------------------
1074 // This function checks the block attributes, of which some might be actually
1075 // particle attributes since a flexible (not standard) attributes naming scheme
1076 // (such as the one adopted in cosmological datasets) causes this Enzo reader,
1077 // specifically function GetAttributeNames(), to take particle attributes as
1078 // block attributes. This function detects and corrects such problems, if any.
1079 // Invalid block attributes are removed, possibly re-considered particle ones.
CheckAttributeNames()1080 void vtkEnzoReaderInternal::CheckAttributeNames()
1081 {
1082   // number of cells of the reference block
1083   vtkEnzoReaderBlock &
1084                 theBlock = this->Blocks[ this->ReferenceBlock ];
1085   int           numCells = theBlock.BlockCellDimensions[0] *
1086                            theBlock.BlockCellDimensions[1] *
1087                            theBlock.BlockCellDimensions[2];
1088 
1089 
1090   // number of particles of the reference block, if any
1091 //  std::cout << "Reference Block: " << this->ReferenceBlock << std::endl;
1092 //  std::cout << "BlockIdx: " << this->ReferenceBlock - 1 << std::endl;
1093 //  std::cout.flush();
1094 
1095   vtkPolyData * polyData = vtkPolyData::New();
1096 //  this->TheReader->GetParticles( this->ReferenceBlock - 1, polyData, 0, 0 );
1097 
1098   int           numbPnts = polyData->GetNumberOfPoints();
1099   polyData->Delete();
1100   polyData = nullptr;
1101 
1102   // block attributes to be removed and / or exported
1103   std::vector < std::string > toRemove;
1104   std::vector < std::string > toExport;
1105   toRemove.clear();
1106   toExport.clear();
1107 
1108   // determine to-be-removed and to-be-exported block attributes
1109   int   i;
1110   int   blockAttrs = static_cast < int > ( this->BlockAttributeNames.size() );
1111   for ( i = 0; i < blockAttrs; i ++ )
1112   {
1113     // the actual number of tuples of a block attribute loaded from the
1114     // file for the reference block
1115     int   numTupls = 0;
1116     if( this->DataArray != nullptr )
1117     {
1118       numTupls = this->DataArray->GetNumberOfTuples();
1119       this->ReleaseDataArray();
1120     }
1121     else
1122     {
1123       numTupls = 0;
1124     }
1125 
1126     // compare the three numbers
1127     if ( numTupls != numCells )
1128     {
1129       if ( numTupls == numbPnts )
1130       {
1131         toExport.push_back( this->BlockAttributeNames[i] );
1132       }
1133       else
1134       {
1135         toRemove.push_back( this->BlockAttributeNames[i] );
1136       }
1137     }
1138   }
1139 
1140   int  nRemoves = static_cast < int > ( toRemove.size() );
1141   int  nExports = static_cast < int > ( toExport.size() );
1142 
1143   // remove block attributes
1144   for ( i = 0; i < nRemoves; i ++ )
1145   {
1146     for ( std::vector < std::string >::iterator
1147           stringIt  = this->BlockAttributeNames.begin();
1148           stringIt != this->BlockAttributeNames.end();
1149           ++stringIt
1150         )
1151     {
1152       if (  ( *stringIt )  ==  toRemove[i]  )
1153       {
1154         this->BlockAttributeNames.erase( stringIt );
1155         break;
1156       }
1157     }
1158   }
1159 
1160   // export attributes from blocks to particles
1161   for ( i = 0; i < nExports; i ++ )
1162   {
1163     for ( std::vector < std::string >::iterator
1164           stringIt  = this->BlockAttributeNames.begin();
1165           stringIt != this->BlockAttributeNames.end();
1166           ++stringIt
1167         )
1168     {
1169       if (  ( *stringIt )  ==  toExport[i]  )
1170       {
1171         this->ParticleAttributeNames.push_back( *stringIt );
1172         this->BlockAttributeNames.erase( stringIt );
1173         break;
1174       }
1175     }
1176   }
1177 
1178   toRemove.clear();
1179   toExport.clear();
1180 }
1181 
1182 // ----------------------------------------------------------------------------
1183 // get the meta data
ReadMetaData()1184 void vtkEnzoReaderInternal::ReadMetaData()
1185 {
1186   // Check to see if we have read it
1187   if ( this->NumberOfBlocks > 0 )
1188   {
1189     return;
1190   }
1191 
1192   // get the general parameters (number of dimensions)
1193   this->ReadGeneralParameters();
1194 
1195   // obtain the block structures
1196   this->ReadBlockStructures();
1197 
1198   // determine the bounding box of the root block
1199   this->DetermineRootBoundingBox();
1200 
1201   // get the parent-wise and level-based bounding Ids of each block in a
1202   // top-down manner
1203   int   blocks = static_cast < int > ( this->Blocks.size() );
1204   for ( int i = 1; i < blocks; i ++ )
1205   {
1206     this->Blocks[i].GetParentWiseIds( this->Blocks );
1207     this->Blocks[i].GetLevelBasedIds( this->Blocks );
1208   }
1209 
1210   // locate the block that contains the fewest cells (either with or without
1211   // particles) and collect the attribute names
1212 
1213   this->GetAttributeNames();
1214 
1215   // verify the initial set of attribute names
1216   this->CheckAttributeNames();
1217 
1218 }
1219