/*========================================================================= Program: Visualization Toolkit Module: vtkAMREnzoParticlesReader.cxx Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkAMREnzoParticlesReader.h" #include "vtkObjectFactory.h" #include "vtkPolyData.h" #include "vtkCellArray.h" #include "vtkDataArraySelection.h" #include "vtkPointData.h" #include "vtkIntArray.h" #include "vtkDataArray.h" #include #include #include "vtksys/SystemTools.hxx" #define H5_USE_16_API #include "vtk_hdf5.h" // for the HDF data loading engine #include "vtkAMREnzoReaderInternal.h" //------------------------------------------------------------------------------ // HDF5 Utility Routines //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // Description: // Finds the block index (blockIndx) within the HDF5 file associated with // the given file index. static bool FindBlockIndex( hid_t fileIndx, const int blockIdx, hid_t &rootIndx ) { // retrieve the contents of the root directory to look for a group // corresponding to the target block, if available, open that group hsize_t numbObjs; rootIndx = H5Gopen( fileIndx, "/" ); if( rootIndx < 0 ) { vtkGenericWarningMacro( "Failed to open root node of particles file" ); return false; } bool found = false; H5Gget_num_objs( rootIndx, &numbObjs ); for( int objIndex=0; objIndex < static_cast < int >(numbObjs); objIndex++ ) { if( H5Gget_objtype_by_idx( rootIndx, objIndex ) == H5G_GROUP ) { int blckIndx; char blckName[65]; H5Gget_objname_by_idx( rootIndx, objIndex, blckName, 64 ); // Is this the target block? if( (sscanf( blckName, "Grid%d", &blckIndx )==1) && (blckIndx == blockIdx) ) { // located the target block rootIndx = H5Gopen( rootIndx, blckName ); if( rootIndx < 0 ) { vtkGenericWarningMacro( "Could not locate target block!\n" ); } found = true; break; } } // END if group } // END for all objects return( found ); } //------------------------------------------------------------------------------ // Description: // Returns the double array static void GetDoubleArrayByName( const hid_t rootIdx, const char* name, std::vector &array) { // turn off warnings void * pContext = nullptr; H5E_auto_t erorFunc; H5Eget_auto( &erorFunc, &pContext ); H5Eset_auto( nullptr, nullptr ); hid_t arrayIdx = H5Dopen( rootIdx, name ); if( arrayIdx < 0 ) { vtkGenericWarningMacro( "Cannot open array: " << name << "\n"); return; } // turn warnings back on H5Eset_auto( erorFunc, pContext ); pContext = nullptr; // get the number of particles hsize_t dimValus[3]; hid_t spaceIdx = H5Dget_space( arrayIdx ); H5Sget_simple_extent_dims( spaceIdx, dimValus, nullptr ); int numbPnts = dimValus[0]; array.resize( numbPnts ); H5Dread( arrayIdx,H5T_NATIVE_DOUBLE,H5S_ALL,H5S_ALL,H5P_DEFAULT,&array[0] ); // H5Dclose( spaceIdx ); // H5Dclose( arrayIdx ); } //------------------------------------------------------------------------------ // END of HDF5 Utility Routine definitions //------------------------------------------------------------------------------ vtkStandardNewMacro(vtkAMREnzoParticlesReader); //------------------------------------------------------------------------------ vtkAMREnzoParticlesReader::vtkAMREnzoParticlesReader() { this->Internal = new vtkEnzoReaderInternal(); this->ParticleType = -1; /* undefined particle type */ this->Initialize(); } //------------------------------------------------------------------------------ vtkAMREnzoParticlesReader::~vtkAMREnzoParticlesReader() { delete this->Internal; this->Internal = nullptr; } //------------------------------------------------------------------------------ void vtkAMREnzoParticlesReader::PrintSelf( std::ostream &os, vtkIndent indent ) { this->Superclass::PrintSelf( os, indent ); } //------------------------------------------------------------------------------ void vtkAMREnzoParticlesReader::ReadMetaData() { if( this->Initialized ) { return; } if( !this->FileName ) { vtkErrorMacro("No FileName set!"); return; } this->Internal->SetFileName( this->FileName ); std::string tempName( this->FileName ); std::string bExtName( ".boundary" ); std::string hExtName( ".hierarchy" ); if( tempName.length() > hExtName.length() && tempName.substr(tempName.length()-hExtName.length() )== hExtName ) { this->Internal->MajorFileName = tempName.substr( 0, tempName.length() - hExtName.length() ); this->Internal->HierarchyFileName = tempName; this->Internal->BoundaryFileName = this->Internal->MajorFileName + bExtName; } else if( tempName.length() > bExtName.length() && tempName.substr( tempName.length() - bExtName.length() )==bExtName ) { this->Internal->MajorFileName = tempName.substr( 0, tempName.length() - bExtName.length() ); this->Internal->BoundaryFileName = tempName; this->Internal->HierarchyFileName = this->Internal->MajorFileName + hExtName; } else { vtkErrorMacro( "Enzo file has invalid extension!"); return; } this->Internal->DirectoryName = GetEnzoDirectory(this->Internal->MajorFileName.c_str()); this->Internal->ReadMetaData(); this->Internal->CheckAttributeNames(); this->NumberOfBlocks = this->Internal->NumberOfBlocks; this->Initialized = true; this->SetupParticleDataSelections(); } //------------------------------------------------------------------------------ vtkDataArray* vtkAMREnzoParticlesReader::GetParticlesTypeArray( const int blockIdx ) { vtkIntArray *array = vtkIntArray::New(); if( this->ParticleDataArraySelection->ArrayExists( "particle_type" ) ) { this->Internal->LoadAttribute( "particle_type", blockIdx ); array->DeepCopy( this->Internal->DataArray ); } return( array ); } //------------------------------------------------------------------------------ bool vtkAMREnzoParticlesReader::CheckParticleType( const int idx, vtkIntArray *ptypes ) { assert( "pre: particles type array should not be nullptr" && (ptypes != nullptr) ); if( ptypes->GetNumberOfTuples() > 0 && this->ParticleDataArraySelection->ArrayExists( "particle_type" ) ) { int ptype = ptypes->GetValue( idx ); if( (this->ParticleType==0) || (ptype==this->ParticleType) ) { return true; } else { return false; } } else { return true; } } //------------------------------------------------------------------------------ vtkPolyData* vtkAMREnzoParticlesReader::GetParticles( const char* file, const int blockIdx ) { vtkPolyData *particles = vtkPolyData::New(); vtkPoints *positions = vtkPoints::New(); positions->SetDataTypeToDouble(); vtkPointData *pdata = particles->GetPointData(); hid_t fileIndx = H5Fopen( file, H5F_ACC_RDONLY, H5P_DEFAULT ); if( fileIndx < 0 ) { vtkErrorMacro( "Failed opening particles file!" ); return nullptr; } hid_t rootIndx; if( ! FindBlockIndex( fileIndx, blockIdx+1,rootIndx ) ) { vtkErrorMacro( "Could not locate target block!" ); return nullptr; } // // Load the particles position arrays by name. // In Enzo the following arrays are available: // ( 1 ) particle_position_i // ( 2 ) tracer_particle_position_i // // where i \in {x,y,z}. std::vector< double > xcoords; std::vector< double > ycoords; std::vector< double > zcoords; // TODO: should we handle 2-D particle datasets? GetDoubleArrayByName( rootIndx, "particle_position_x", xcoords ); GetDoubleArrayByName( rootIndx, "particle_position_y", ycoords ); GetDoubleArrayByName( rootIndx, "particle_position_z", zcoords ); vtkIntArray *particleTypes = vtkArrayDownCast( this->GetParticlesTypeArray( blockIdx ) ); assert( "Coordinate arrays must have the same size: " && (xcoords.size()==ycoords.size() ) ); assert( "Coordinate arrays must have the same size: " && (ycoords.size()==zcoords.size() ) ); int TotalNumberOfParticles = static_cast(xcoords.size()); positions->SetNumberOfPoints( TotalNumberOfParticles ); vtkIdList *ids = vtkIdList::New(); ids->SetNumberOfIds( TotalNumberOfParticles ); vtkIdType NumberOfParticlesLoaded = 0; for( int i=0; i < TotalNumberOfParticles; ++i ) { if( (i%this->Frequency) == 0 ) { if( this->CheckLocation( xcoords[i], ycoords[i],zcoords[i] ) && this->CheckParticleType( i, particleTypes) ) { int pidx = NumberOfParticlesLoaded; ids->InsertId( pidx, i ); positions->SetPoint( pidx, xcoords[i],ycoords[i],zcoords[i] ); ++NumberOfParticlesLoaded; } // END if within requested region } // END if within requested interval } // END for all particles H5Gclose( rootIndx ); H5Fclose( fileIndx ); ids->SetNumberOfIds( NumberOfParticlesLoaded ); ids->Squeeze(); positions->SetNumberOfPoints( NumberOfParticlesLoaded ); positions->Squeeze(); particles->SetPoints( positions ); positions->Delete(); // Create CellArray consisting of a single polyvertex cell vtkCellArray *polyVertex = vtkCellArray::New(); polyVertex->InsertNextCell( NumberOfParticlesLoaded ); for( vtkIdType idx=0; idx < NumberOfParticlesLoaded; ++idx ) polyVertex->InsertCellPoint( idx ); particles->SetVerts( polyVertex ); polyVertex->Delete(); // Release the particle types array particleTypes->Delete(); int numArrays = this->ParticleDataArraySelection->GetNumberOfArrays(); for( int i=0; i < numArrays; ++i ) { const char* name = this->ParticleDataArraySelection->GetArrayName( i ); if( this->ParticleDataArraySelection->ArrayIsEnabled( name ) ) { // Note: 0-based indexing is used for loading particles this->Internal->LoadAttribute( name, blockIdx ); assert( "pre: particle attribute size mismatch" && (this->Internal->DataArray->GetNumberOfTuples()== TotalNumberOfParticles) ); vtkDataArray *array = this->Internal->DataArray->NewInstance(); array->SetName(this->Internal->DataArray->GetName( ) ); array->SetNumberOfTuples( NumberOfParticlesLoaded ); array->SetNumberOfComponents( this->Internal->DataArray->GetNumberOfComponents() ); vtkIdType numIds = ids->GetNumberOfIds(); for( vtkIdType pidx=0; pidx < numIds; ++pidx ) { vtkIdType particleIdx = ids->GetId( pidx ); int numComponents = array->GetNumberOfComponents(); for( int k=0; k < numComponents; ++k ) { array->SetComponent( pidx, k, this->Internal->DataArray->GetComponent( particleIdx,k ) ); } // END for all components } // END for all ids of loaded particles pdata->AddArray( array ); array->Delete(); } // END if the array is supposed to be loaded } // END for all particle arrays ids->Delete(); return( particles ); } //------------------------------------------------------------------------------ void vtkAMREnzoParticlesReader::SetupParticleDataSelections() { assert( "pre: Intenal reader is nullptr" && (this->Internal != nullptr) ); unsigned int N = static_cast( this->Internal->ParticleAttributeNames.size() ); for( unsigned int i=0; i < N; ++i ) { bool isParticleAttribute = vtksys::SystemTools::StringStartsWith( this->Internal->ParticleAttributeNames[ i ].c_str( ), "particle_" ); if( isParticleAttribute ) { this->ParticleDataArraySelection->AddArray( this->Internal->ParticleAttributeNames[ i ].c_str() ); } } // END for all particles attributes this->InitializeParticleDataSelections(); } //------------------------------------------------------------------------------ int vtkAMREnzoParticlesReader::GetTotalNumberOfParticles( ) { assert( "Internal reader is null" && (this->Internal!=nullptr) ); int numParticles = 0; for( int blockIdx=0; blockIdx < this->NumberOfBlocks; ++blockIdx ) { numParticles += this->Internal->Blocks[ blockIdx ].NumberOfParticles; } return( numParticles ); } //------------------------------------------------------------------------------ vtkPolyData* vtkAMREnzoParticlesReader::ReadParticles(const int blkidx) { // this->Internal->Blocks includes a pseudo block -- the roo as block #0 int iBlockIdx = blkidx+1; int NumParticles = this->Internal->Blocks[ iBlockIdx ].NumberOfParticles; if( NumParticles <= 0 ) { vtkPolyData* emptyParticles = vtkPolyData::New(); assert( "Cannot create particles dataset" && ( emptyParticles != nullptr ) ); return( emptyParticles ); } std::string pfile = this->Internal->Blocks[iBlockIdx].ParticleFileName; if( pfile.empty() ) { vtkErrorMacro( "No particles file found, string is empty!" ); return nullptr; } vtkPolyData* particles = this->GetParticles(pfile.c_str(), blkidx ); return( particles ); }