1 /*=========================================================================
2 
3  Program:   Visualization Toolkit
4  Module:    vtkAMRFlashParticlesReader.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 "vtkAMRFlashParticlesReader.h"
16 #include "vtkObjectFactory.h"
17 #include "vtkPolyData.h"
18 #include "vtkDataArraySelection.h"
19 #include "vtkIdList.h"
20 #include "vtkCellArray.h"
21 #include "vtkDoubleArray.h"
22 #include "vtkIntArray.h"
23 #include "vtkPointData.h"
24 
25 #include "vtkAMRFlashReaderInternal.h"
26 
27 #define H5_USE_16_API
28 #include "vtk_hdf5.h"      // for the HDF data loading engine
29 
30 #include <vector>
31 #include <cassert>
32 
33 #define  FLASH_READER_MAX_DIMS     3
34 #define  FLASH_READER_LEAF_BLOCK   1
35 #define  FLASH_READER_FLASH3_FFV8  8
36 #define  FLASH_READER_FLASH3_FFV9  9
37 
38 //------------------------------------------------------------------------------
39 // Description:
40 // Helper function that reads the particle coordinates
41 // NOTE: it is assumed that H5DOpen has been called on the
42 // internal file index this->FileIndex.
GetParticleCoordinates(hid_t & dataIdx,std::vector<double> & xcoords,std::vector<double> & ycoords,std::vector<double> & zcoords,vtkFlashReaderInternal * iReader,int NumParticles)43 static void GetParticleCoordinates( hid_t &dataIdx,
44   std::vector< double > &xcoords, std::vector< double > &ycoords,
45   std::vector< double > &zcoords,
46   vtkFlashReaderInternal *iReader,
47   int NumParticles )
48 {
49 
50   assert( "pre: internal reader should not be nullptr" && (iReader != nullptr) );
51 
52   hid_t theTypes[3];
53   theTypes[0] = theTypes[1] = theTypes[2] = H5I_UNINIT;
54   xcoords.resize( NumParticles );
55   ycoords.resize( NumParticles );
56   zcoords.resize( NumParticles );
57 
58   if( iReader->FileFormatVersion < FLASH_READER_FLASH3_FFV8  )
59   {
60     theTypes[0] = H5Tcreate( H5T_COMPOUND, sizeof(double) );
61     theTypes[1] = H5Tcreate( H5T_COMPOUND, sizeof(double) );
62     theTypes[2] = H5Tcreate( H5T_COMPOUND, sizeof(double) );
63     H5Tinsert( theTypes[0], "particle_x", 0, H5T_NATIVE_DOUBLE );
64     H5Tinsert( theTypes[1], "particle_y", 0, H5T_NATIVE_DOUBLE );
65     H5Tinsert( theTypes[2], "particle_z", 0, H5T_NATIVE_DOUBLE );
66   }
67 
68   // Read the coordinates from the file
69   switch( iReader->NumberOfDimensions )
70   {
71     case 1:
72       if( iReader->FileFormatVersion < FLASH_READER_FLASH3_FFV8 )
73       {
74         H5Dread(dataIdx,theTypes[0],H5S_ALL,H5S_ALL,H5P_DEFAULT,&xcoords[0]);
75       }
76       else
77       {
78         iReader->ReadParticlesComponent(dataIdx,"Particles/posx",&xcoords[0]);
79       }
80       break;
81     case 2:
82       if( iReader->FileFormatVersion < FLASH_READER_FLASH3_FFV8 )
83       {
84         H5Dread(dataIdx,theTypes[0],H5S_ALL,H5S_ALL,H5P_DEFAULT,&xcoords[0]);
85         H5Dread(dataIdx,theTypes[1],H5S_ALL,H5S_ALL,H5P_DEFAULT,&ycoords[0]);
86       }
87       else
88       {
89         iReader->ReadParticlesComponent(dataIdx,"Particles/posx",&xcoords[0]);
90         iReader->ReadParticlesComponent(dataIdx,"Particles/posy",&ycoords[0]);
91       }
92       break;
93     case 3:
94       if( iReader->FileFormatVersion < FLASH_READER_FLASH3_FFV8 )
95       {
96         H5Dread(dataIdx,theTypes[0],H5S_ALL,H5S_ALL,H5P_DEFAULT,&xcoords[0]);
97         H5Dread(dataIdx,theTypes[1],H5S_ALL,H5S_ALL,H5P_DEFAULT,&ycoords[0]);
98         H5Dread(dataIdx,theTypes[2],H5S_ALL,H5S_ALL,H5P_DEFAULT,&zcoords[0]);
99       }
100       else
101       {
102         iReader->ReadParticlesComponent(dataIdx,"Particles/posx",&xcoords[0]);
103         iReader->ReadParticlesComponent(dataIdx,"Particles/posy",&ycoords[0]);
104         iReader->ReadParticlesComponent(dataIdx,"Particles/posz",&zcoords[0]);
105       }
106       break;
107     default:
108       std::cerr << "ERROR: Undefined dimension!\n" << std::endl;
109       std::cerr.flush();
110       return;
111   }
112 
113 }
114 
115 
116 //------------------------------------------------------------------------------
117 
118 
119 //------------------------------------------------------------------------------
120 
121 vtkStandardNewMacro( vtkAMRFlashParticlesReader );
122 
vtkAMRFlashParticlesReader()123 vtkAMRFlashParticlesReader::vtkAMRFlashParticlesReader()
124 {
125   this->Internal = new vtkFlashReaderInternal();
126   this->Initialized = false;
127   this->Initialize();
128 }
129 
130 //------------------------------------------------------------------------------
~vtkAMRFlashParticlesReader()131 vtkAMRFlashParticlesReader::~vtkAMRFlashParticlesReader()
132 {
133   delete this->Internal;
134 }
135 
136 //------------------------------------------------------------------------------
PrintSelf(std::ostream & os,vtkIndent indent)137 void vtkAMRFlashParticlesReader::PrintSelf( std::ostream &os, vtkIndent indent )
138 {
139   this->Superclass::PrintSelf( os, indent );
140 }
141 
142 //------------------------------------------------------------------------------
ReadMetaData()143 void vtkAMRFlashParticlesReader::ReadMetaData()
144 {
145   if( this->Initialized )
146   {
147     return;
148   }
149 
150   this->Internal->SetFileName( this->FileName );
151   this->Internal->ReadMetaData();
152 
153   // In some cases, the FLASH file format may have no blocks but store
154   // just particles in a single block. However, the AMRBaseParticles reader
155   // would expect that in this case, the number of blocks is set to 1. The
156   // following lines of code provide a simple workaround for that.
157   this->NumberOfBlocks = this->Internal->NumberOfBlocks;
158   if( this->NumberOfBlocks == 0 && this->Internal->NumberOfParticles > 0)
159   {
160     this->NumberOfBlocks=1;
161   }
162   this->Initialized    = true;
163   this->SetupParticleDataSelections();
164 }
165 
166 //------------------------------------------------------------------------------
GetTotalNumberOfParticles()167 int vtkAMRFlashParticlesReader::GetTotalNumberOfParticles()
168 {
169   assert( "Internal reader is null" && (this->Internal!=nullptr) );
170   return( this->Internal->NumberOfParticles );
171 }
172 
173 //------------------------------------------------------------------------------
GetParticles(const char * file,const int vtkNotUsed (blkidx))174 vtkPolyData* vtkAMRFlashParticlesReader::GetParticles(
175     const char *file, const int vtkNotUsed(blkidx) )
176 {
177   hid_t dataIdx = H5Dopen( this->Internal->FileIndex, file );
178   if( dataIdx < 0 )
179   {
180     vtkErrorMacro( "Could not open particles file!" );
181     return nullptr;
182   }
183 
184   vtkPolyData *particles = vtkPolyData::New();
185   vtkPoints   *positions = vtkPoints::New();
186   positions->SetDataTypeToDouble();
187   positions->SetNumberOfPoints( this->Internal->NumberOfParticles );
188 
189   vtkPointData *pdata = particles->GetPointData();
190   assert( "pre: PointData is nullptr" && (pdata != nullptr) );
191 
192   // Load the particle position arrays by name
193   std::vector< double > xcoords;
194   std::vector< double > ycoords;
195   std::vector< double > zcoords;
196   GetParticleCoordinates(
197       dataIdx, xcoords, ycoords, zcoords,
198       this->Internal, this->Internal->NumberOfParticles );
199 
200   // Sub-sample particles
201   int TotalNumberOfParticles        = static_cast<int>(xcoords.size());
202   vtkIdList *ids                    = vtkIdList::New();
203   ids->SetNumberOfIds( TotalNumberOfParticles );
204 
205   vtkIdType NumberOfParticlesLoaded = 0;
206   for( int i=0; i < TotalNumberOfParticles; ++i )
207   {
208     if( i%this->Frequency == 0 )
209     {
210       if( this->CheckLocation(xcoords[i],ycoords[i],zcoords[i] ) )
211       {
212         int pidx = NumberOfParticlesLoaded;
213         ids->InsertId( pidx, i );
214         positions->SetPoint( pidx, xcoords[i], ycoords[i], zcoords[i] );
215         ++NumberOfParticlesLoaded;
216       } // END if within requested region
217     } // END if within requested interval
218   } // END for all particles
219 
220   xcoords.clear(); ycoords.clear(); zcoords.clear();
221 
222   ids->SetNumberOfIds( NumberOfParticlesLoaded );
223   ids->Squeeze();
224 
225   positions->SetNumberOfPoints( NumberOfParticlesLoaded );
226   positions->Squeeze();
227 
228   particles->SetPoints( positions );
229   positions->Squeeze( );
230 
231   // Create CellArray consisting of a single polyvertex
232   vtkCellArray *polyVertex = vtkCellArray::New();
233   polyVertex ->InsertNextCell( NumberOfParticlesLoaded );
234   for( vtkIdType idx=0; idx < NumberOfParticlesLoaded; ++idx )
235   {
236     polyVertex->InsertCellPoint( idx );
237   }
238   particles->SetVerts( polyVertex );
239   polyVertex->Delete();
240 
241   // Load particle data arrays
242   int numArrays = this->ParticleDataArraySelection->GetNumberOfArrays();
243   for( int i=0; i < numArrays; ++i )
244   {
245     const char *name = this->ParticleDataArraySelection->GetArrayName( i );
246     if( this->ParticleDataArraySelection->ArrayIsEnabled( name ) )
247     {
248       int attrIdx     = this->Internal->ParticleAttributeNamesToIds[ name ];
249       hid_t attrType  = this->Internal->ParticleAttributeTypes[ attrIdx ];
250 
251       if( attrType == H5T_NATIVE_DOUBLE )
252       {
253         double *data = new double[ this->Internal->NumberOfParticles ];
254         assert( data != nullptr );
255 
256         if( this->Internal->FileFormatVersion < FLASH_READER_FLASH3_FFV8 )
257         {
258           hid_t dataType = H5Tcreate( H5T_COMPOUND, sizeof(double) );
259           H5Tinsert( dataType, name, 0, H5T_NATIVE_DOUBLE );
260           H5Dread(dataIdx, dataType, H5S_ALL, H5S_ALL, H5P_DEFAULT,data);
261           H5Tclose( dataType );
262         }
263         else
264         {
265           this->Internal->ReadParticlesComponent( dataIdx, name, data );
266         }
267 
268         vtkDataArray *array = vtkDoubleArray::New();
269         array->SetName( name );
270         array->SetNumberOfTuples( ids->GetNumberOfIds() );
271         array->SetNumberOfComponents( 1 );
272 
273         vtkIdType numIds = ids->GetNumberOfIds();
274         for( vtkIdType pidx=0; pidx < numIds; ++pidx )
275         {
276           vtkIdType particleIdx = ids->GetId( pidx );
277           array->SetComponent( pidx, 0, data[ particleIdx ] );
278         } // END for all ids of loaded particles
279         pdata->AddArray( array );
280         delete [] data;
281       }
282       else if( attrType == H5T_NATIVE_INT )
283       {
284         hid_t dataType = H5Tcreate( H5T_COMPOUND, sizeof(int) );
285         H5Tinsert( dataType, name, 0, H5T_NATIVE_INT );
286 
287         int *data = new int[ this->Internal->NumberOfParticles ];
288         assert( data != nullptr );
289         H5Dread( dataIdx, dataType, H5S_ALL, H5S_ALL, H5P_DEFAULT, data );
290 
291         vtkDataArray *array = vtkIntArray::New();
292         array->SetName( name );
293         array->SetNumberOfTuples( ids->GetNumberOfIds() );
294         array->SetNumberOfComponents( 1 );
295 
296         vtkIdType numIds = ids->GetNumberOfIds( );
297         for( vtkIdType pidx=0; pidx < numIds; ++pidx )
298         {
299           vtkIdType particleIdx = ids->GetId( pidx );
300           array->SetComponent( pidx, 0, data[ particleIdx ] );
301         } // END for all ids of loaded particles
302         pdata->AddArray( array );
303         delete [] data;
304       }
305       else
306       {
307         vtkErrorMacro( "Unsupported array type in HDF5 file!" );
308         return nullptr;
309       }
310     } // END if the array is supposed to be loaded
311   } // END for all arrays
312 
313   H5Dclose(dataIdx);
314 
315   return( particles );
316 }
317 
318 //------------------------------------------------------------------------------
ReadParticles(const int blkidx)319 vtkPolyData* vtkAMRFlashParticlesReader::ReadParticles( const int blkidx )
320 {
321   assert( "pre: Internal reader is nullptr" && (this->Internal != nullptr) );
322   assert( "pre: Not initialized " && (this->Initialized) );
323 
324   int NumberOfParticles = this->Internal->NumberOfParticles;
325   if( NumberOfParticles <= 0 )
326   {
327     vtkPolyData *emptyParticles = vtkPolyData::New();
328     assert( "Cannot create particle dataset" && (emptyParticles != nullptr)  );
329     return( emptyParticles );
330   }
331 
332   vtkPolyData* particles = this->GetParticles(
333      this->Internal->ParticleName.c_str(), blkidx );
334   assert( "partciles should not be nullptr " && (particles != nullptr) );
335 
336   return( particles );
337 }
338 
339 //------------------------------------------------------------------------------
SetupParticleDataSelections()340 void vtkAMRFlashParticlesReader::SetupParticleDataSelections()
341 {
342   assert( "pre: Internal reader is nullptr" && (this->Internal != nullptr) );
343 
344   unsigned int N =
345       static_cast<unsigned int>(this->Internal->ParticleAttributeNames.size());
346   for( unsigned int i=0; i < N; ++i )
347   {
348     this->ParticleDataArraySelection->AddArray(
349       this->Internal->ParticleAttributeNames[ i ].c_str( ) );
350   } // END for all particles attributes
351 
352   this->InitializeParticleDataSelections();
353 }
354