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