/*========================================================================= Program: Visualization Toolkit Module: vtkHDFReaderImplementation.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 "vtkHDFReaderImplementation.h" #include #include #include #include #include #include "vtkCharArray.h" #include "vtkDataArrayRange.h" #include "vtkDataObject.h" #include "vtkDoubleArray.h" #include "vtkFloatArray.h" #include "vtkIdTypeArray.h" #include "vtkIntArray.h" #include "vtkLogger.h" #include "vtkLongArray.h" #include "vtkLongLongArray.h" #include "vtkShortArray.h" #include "vtkStringArray.h" #include "vtkUnsignedCharArray.h" #include "vtkUnsignedIntArray.h" #include "vtkUnsignedLongArray.h" #include "vtkUnsignedLongLongArray.h" #include "vtkUnsignedShortArray.h" //------------------------------------------------------------------------------ namespace { herr_t AddName(hid_t group, const char* name, const H5L_info_t*, void* op_data) { auto array = static_cast*>(op_data); herr_t status = -1; H5O_info_t infobuf; if ((status = H5Oget_info_by_name(group, name, &infobuf, H5P_DEFAULT)) >= 0 && infobuf.type == H5O_TYPE_DATASET) { array->push_back(name); } return status; } }; //------------------------------------------------------------------------------ vtkHDFReader::Implementation::TypeDescription vtkHDFReader::Implementation::GetTypeDescription( hid_t type) { TypeDescription td; td.Class = H5Tget_class(type); td.Size = H5Tget_size(type); if (td.Class == H5T_INTEGER) { td.Sign = H5Tget_sign(type); } return td; } //------------------------------------------------------------------------------ vtkHDFReader::Implementation::Implementation(vtkHDFReader* reader) : File(-1) , VTKGroup(-1) , DataSetType(-1) , NumberOfPieces(-1) , Reader(reader) { std::fill(this->AttributeDataGroup.begin(), this->AttributeDataGroup.end(), -1); std::fill(this->Version.begin(), this->Version.end(), 0); } //------------------------------------------------------------------------------ vtkHDFReader::Implementation::~Implementation() { this->Close(); } std::vector vtkHDFReader::Implementation::GetDimensions(const char* datasetName) { bool error = false; hid_t dataset = -1; hid_t dataspace = -1; std::vector dims; try { if ((dataset = H5Dopen(this->File, datasetName, H5P_DEFAULT)) < 0) { throw std::runtime_error(std::string("Cannot open ") + datasetName); } if ((dataspace = H5Dget_space(dataset)) < 0) { throw std::runtime_error(std::string("Cannot get space for dataset ") + datasetName); } int rank; if ((rank = H5Sget_simple_extent_ndims(dataspace)) < 0) { throw std::runtime_error( std::string(datasetName) + " dataset: get_simple_extent_ndims error"); } std::vector dimsFile(rank); if (H5Sget_simple_extent_dims(dataspace, &dimsFile[0], nullptr) < 0) { throw std::runtime_error(std::string("Cannot find dimension for ") + datasetName); } dims = dimsFile; } catch (std::exception& e) { vtkErrorWithObjectMacro(this->Reader, << e.what()); error = true; } if (dataspace >= 0) { error = H5Sclose(dataspace) < 0 || error; } if (dataset >= 0) { error = H5Dclose(dataset) < 0 || error; } return dims; } //------------------------------------------------------------------------------ bool vtkHDFReader::Implementation::Open(const char* fileName) { bool error = false; if (!fileName) { vtkErrorWithObjectMacro(this->Reader, "Invalid filename: " << fileName); return false; } if (this->FileName.empty() || this->FileName != fileName) { this->FileName = fileName; if (this->File >= 0) { this->Close(); } // turn off error logging and save error function H5E_auto_t f; void* client_data; H5Eget_auto(H5E_DEFAULT, &f, &client_data); H5Eset_auto(H5E_DEFAULT, nullptr, nullptr); if ((this->File = H5Fopen(this->FileName.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT)) < 0) { // we try to read a non-HDF file return false; } if ((this->VTKGroup = H5Gopen(this->File, "/VTKHDF", H5P_DEFAULT)) < 0) { // we try to read a non-VTKHDF file return false; } std::array groupNames = { "/VTKHDF/PointData", "/VTKHDF/CellData", "/VTKHDF/FieldData" }; // try to open cell or point group. Its OK if they don't exist. for (size_t i = 0; i < this->AttributeDataGroup.size(); ++i) { this->AttributeDataGroup[i] = H5Gopen(this->File, groupNames[i], H5P_DEFAULT); } // turn on error logging and restore error function H5Eset_auto(H5E_DEFAULT, f, client_data); if (!GetAttribute("Version", this->Version.size(), &this->Version[0])) { return false; } hid_t attr = -1; try { H5Eset_auto(H5E_DEFAULT, nullptr, nullptr); // errors off if ((attr = H5Aopen_name(this->VTKGroup, "WholeExtent")) < 0) { H5Eset_auto(H5E_DEFAULT, f, client_data); // errors on this->DataSetType = VTK_UNSTRUCTURED_GRID; const char* datasetName = "/VTKHDF/NumberOfPoints"; std::vector dims = this->GetDimensions(datasetName); if (dims.size() != 1) { throw std::runtime_error(std::string(datasetName) + " dataset should have 1 dimension"); } this->NumberOfPieces = dims[0]; } else { H5Eset_auto(H5E_DEFAULT, f, client_data); // errors on this->DataSetType = VTK_IMAGE_DATA; this->NumberOfPieces = 1; } } catch (const std::exception& e) { vtkErrorWithObjectMacro(this->Reader, << e.what()); error = true; } if (attr >= 0) { error = H5Aclose(attr) < 0 || error; } } this->BuildTypeReaderMap(); return !error; } //------------------------------------------------------------------------------ void vtkHDFReader::Implementation::Close() { this->DataSetType = -1; this->NumberOfPieces = 0; std::fill(this->Version.begin(), this->Version.end(), 0); for (size_t i = 0; i < this->AttributeDataGroup.size(); ++i) { if (this->AttributeDataGroup[i] >= 0) { H5Gclose(this->AttributeDataGroup[i]); this->AttributeDataGroup[i] = -1; } } if (this->VTKGroup >= 0) { H5Gclose(this->VTKGroup); this->VTKGroup = -1; } if (this->File >= 0) { H5Fclose(this->File); this->File = -1; } } //------------------------------------------------------------------------------ void vtkHDFReader::Implementation::BuildTypeReaderMap() { this->TypeReaderMap[this->GetTypeDescription(H5T_NATIVE_CHAR)] = &vtkHDFReader::Implementation::NewArray; this->TypeReaderMap[this->GetTypeDescription(H5T_NATIVE_UCHAR)] = &vtkHDFReader::Implementation::NewArray; this->TypeReaderMap[this->GetTypeDescription(H5T_NATIVE_SHORT)] = &vtkHDFReader::Implementation::NewArray; this->TypeReaderMap[this->GetTypeDescription(H5T_NATIVE_USHORT)] = &vtkHDFReader::Implementation::NewArray; this->TypeReaderMap[this->GetTypeDescription(H5T_NATIVE_INT)] = &vtkHDFReader::Implementation::NewArray; this->TypeReaderMap[this->GetTypeDescription(H5T_NATIVE_UINT)] = &vtkHDFReader::Implementation::NewArray; if (!this->TypeReaderMap[this->GetTypeDescription(H5T_NATIVE_LONG)]) { // long may be the same as int this->TypeReaderMap[this->GetTypeDescription(H5T_NATIVE_LONG)] = &vtkHDFReader::Implementation::NewArray; this->TypeReaderMap[this->GetTypeDescription(H5T_NATIVE_ULONG)] = &vtkHDFReader::Implementation::NewArray; } if (!this->TypeReaderMap[this->GetTypeDescription(H5T_NATIVE_LLONG)]) { // long long may be the same as long this->TypeReaderMap[this->GetTypeDescription(H5T_NATIVE_LLONG)] = &vtkHDFReader::Implementation::NewArray; this->TypeReaderMap[this->GetTypeDescription(H5T_NATIVE_ULLONG)] = &vtkHDFReader::Implementation::NewArray; } this->TypeReaderMap[this->GetTypeDescription(H5T_NATIVE_FLOAT)] = &vtkHDFReader::Implementation::NewArray; this->TypeReaderMap[this->GetTypeDescription(H5T_NATIVE_DOUBLE)] = &vtkHDFReader::Implementation::NewArray; } //------------------------------------------------------------------------------ template hid_t vtkHDFReader::Implementation::TemplateTypeToHdfNativeType() { if (std::is_same::value) { return H5T_NATIVE_CHAR; } else if (std::is_same::value) { return H5T_NATIVE_UCHAR; } else if (std::is_same::value) { return H5T_NATIVE_SHORT; } else if (std::is_same::value) { return H5T_NATIVE_USHORT; } else if (std::is_same::value) { return H5T_NATIVE_INT; } else if (std::is_same::value) { return H5T_NATIVE_UINT; } else if (std::is_same::value) { return H5T_NATIVE_LONG; } else if (std::is_same::value) { return H5T_NATIVE_ULONG; } else if (std::is_same::value) { return H5T_NATIVE_LLONG; } else if (std::is_same::value) { return H5T_NATIVE_ULLONG; } else if (std::is_same::value) { return H5T_NATIVE_FLOAT; } else if (std::is_same::value) { return H5T_NATIVE_DOUBLE; } else { vtkErrorWithObjectMacro(this->Reader, "Invalid type: " << typeid(T).name()); return -1; } } //------------------------------------------------------------------------------ template vtkDataArray* vtkHDFReader::Implementation::NewVtkDataArray() { if (std::is_same::value) { return vtkCharArray::New(); } else if (std::is_same::value) { return vtkUnsignedCharArray::New(); } else if (std::is_same::value) { return vtkShortArray::New(); } else if (std::is_same::value) { return vtkUnsignedShortArray::New(); } else if (std::is_same::value) { return vtkIntArray::New(); } else if (std::is_same::value) { return vtkUnsignedIntArray::New(); } else if (std::is_same::value) { return vtkLongArray::New(); } else if (std::is_same::value) { return vtkUnsignedLongArray::New(); } else if (std::is_same::value) { return vtkLongLongArray::New(); } else if (std::is_same::value) { return vtkUnsignedLongLongArray::New(); } else if (std::is_same::value) { return vtkFloatArray::New(); } else if (std::is_same::value) { return vtkDoubleArray::New(); } else { vtkErrorWithObjectMacro(this->Reader, "Invalid type: " << typeid(T).name()); return nullptr; } } //------------------------------------------------------------------------------ bool vtkHDFReader::Implementation::GetPartitionExtent(hsize_t partitionIndex, int* extent) { hid_t dataset = -1; hid_t memspace = -1; hid_t dataspace = -1; const int RANK = 2; bool error = false; const char* datasetName = "/VTKHDF/Extents"; try { // create the memory space hsize_t dimsm[RANK]; dimsm[0] = 1; dimsm[1] = 6; if ((memspace = H5Screate_simple(RANK, dimsm, nullptr)) < 0) { throw std::runtime_error("Error H5Screate_simple for memory space"); } // create the file dataspace + hyperslab if ((dataset = H5Dopen(this->File, datasetName, H5P_DEFAULT)) < 0) { throw std::runtime_error(std::string("Cannot open ") + datasetName); } hsize_t start[RANK] = { partitionIndex, 0 }, count[RANK] = { 1, 2 }; if ((dataspace = H5Dget_space(dataset)) < 0) { throw std::runtime_error(std::string("Cannot get space for dataset ") + datasetName); } if (H5Sselect_hyperslab(dataspace, H5S_SELECT_SET, start, nullptr, count, nullptr) < 0) { throw std::runtime_error(std::string("Error selecting hyperslab for ") + datasetName); } // read hyperslab if (H5Dread(dataset, H5T_NATIVE_INT, memspace, dataspace, H5P_DEFAULT, extent) < 0) { throw std::runtime_error(std::string("Error reading hyperslab from ") + datasetName); } } catch (const std::exception& e) { vtkErrorWithObjectMacro(this->Reader, << e.what()); error = true; } if (memspace >= 0) { error = H5Sclose(memspace) < 0 || error; } if (dataspace >= 0) { error = H5Sclose(dataspace) < 0 || error; } if (dataset >= 0) { error = H5Dclose(dataset) < 0 || error; } return !error; } //------------------------------------------------------------------------------ template bool vtkHDFReader::Implementation::GetAttribute( const char* attributeName, size_t numberOfElements, T* value) { hid_t attr = -1; hid_t space = -1; bool error = false; try { if ((attr = H5Aopen_name(this->VTKGroup, attributeName)) < 0) { throw std::runtime_error(std::string(attributeName) + " attribute not found"); } if ((space = H5Aget_space(attr)) < 0) { throw std::runtime_error(std::string(attributeName) + " attribute: get_space error"); } int ndims; if ((ndims = H5Sget_simple_extent_ndims(space)) < 0) { throw std::runtime_error( std::string(attributeName) + " attribute: get_simple_extent_ndims error"); } if (ndims != 1) { throw std::runtime_error(std::string(attributeName) + " attribute should have rank 1"); } hsize_t ne; if (H5Sget_simple_extent_dims(space, &ne, nullptr) < 0) { throw std::runtime_error(std::string("Cannot find dimension for ") + attributeName); } if (ne != numberOfElements) { std::ostringstream ostr; ostr << attributeName << " attribute should have " << numberOfElements << " dimensions"; throw std::runtime_error(ostr.str().c_str()); } hid_t hdfType = 0; if ((hdfType = this->TemplateTypeToHdfNativeType()) < 0) { throw std::runtime_error(std::string("Native type not implemented: ") + typeid(T).name()); } if (H5Aread(attr, hdfType, value) < 0) { throw std::runtime_error(std::string("Error reading ") + attributeName + " attribute"); } } catch (const std::exception& e) { vtkErrorWithObjectMacro(this->Reader, << e.what()); error = true; } if (space >= 0) { error = H5Sclose(space) < 0 || error; } if (attr >= 0) { error = H5Aclose(attr) < 0 || error; } return !error; } //------------------------------------------------------------------------------ std::vector vtkHDFReader::Implementation::GetArrayNames(int attributeType) { std::vector array; hid_t group = this->AttributeDataGroup[attributeType]; if (group > 0) { // H5_INDEX_CRT_ORDER failed with: no creation order index to query H5Literate(group, H5_INDEX_NAME, H5_ITER_NATIVE, nullptr, AddName, &array); } return array; } //------------------------------------------------------------------------------ hid_t vtkHDFReader::Implementation::OpenDataSet( hid_t group, const char* name, hid_t* nativeType, std::vector& dims) { bool error = false; hid_t dataset = -1; hid_t dataspace = -1; hid_t datatype = -1; try { if ((dataset = H5Dopen(group, name, H5P_DEFAULT)) < 0) { throw std::runtime_error(std::string("Cannot open ") + name); } if ((datatype = H5Dget_type(dataset)) < 0) { throw std::runtime_error(std::string("Cannot get_type for dataset: ") + name); } if ((dataspace = H5Dget_space(dataset)) < 0) { throw std::runtime_error(std::string("Cannot get space for dataset ") + name); } if ((*nativeType = H5Tget_native_type(datatype, H5T_DIR_ASCEND)) < 0) { throw std::runtime_error(std::string("Cannot get type for dataset ") + name); } int ndims; if ((ndims = H5Sget_simple_extent_ndims(dataspace)) < 0) { throw std::runtime_error(std::string(name) + " dataset: get_simple_extent_ndims error"); } dims.resize(ndims); if (H5Sget_simple_extent_dims(dataspace, &dims[0], nullptr) < 0) { throw std::runtime_error(std::string("Cannot find dimension for ") + name); } } catch (const std::exception& e) { vtkErrorWithObjectMacro(this->Reader, << e.what()); error = true; } if (dataspace >= 0) { error = H5Sclose(dataspace) < 0 || error; } if (datatype >= 0) { error = H5Tclose(datatype) < 0 || error; } if (error && dataset >= 0) { H5Dclose(dataset); dataset = -1; } return dataset; } //------------------------------------------------------------------------------ vtkDataArray* vtkHDFReader::Implementation::NewArray( int attributeType, const char* name, const std::vector& fileExtent) { return NewArray(this->AttributeDataGroup[attributeType], name, fileExtent); } //------------------------------------------------------------------------------ vtkDataArray* vtkHDFReader::Implementation::NewArray( int attributeType, const char* name, hsize_t offset, hsize_t size) { std::vector fileExtent = { offset, offset + size - 1 }; return NewArray(this->AttributeDataGroup[attributeType], name, fileExtent); } //------------------------------------------------------------------------------ vtkStringArray* vtkHDFReader::Implementation::NewStringArray(hid_t dataset, hsize_t size) { std::vector rdata(size); /* * Create the memory datatype. */ hid_t memtype = H5Tcopy(H5T_C_S1); if (H5Tset_size(memtype, H5T_VARIABLE) < 0) { vtkErrorWithObjectMacro(this->Reader, << "Error H5Tset_size"); return nullptr; } /* * Read the data. */ if (H5Dread(dataset, memtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, &rdata[0]) < 0) { vtkErrorWithObjectMacro(this->Reader, << "Error H5Dread"); } auto array = vtkStringArray::New(); array->SetNumberOfTuples(size); for (size_t i = 0; i < size; ++i) { array->SetValue(i, rdata[i]); } /* * Close and release resources. Note that H5Dvlen_reclaim works * for variable-length strings as well as variable-length arrays. * Also note that we must still free the array of pointers stored * in rdata, as H5Tvlen_reclaim only frees the data these point to. */ hid_t space = H5Dget_space(dataset); if (H5Dvlen_reclaim(memtype, space, H5P_DEFAULT, &rdata[0]) < 0) { vtkErrorWithObjectMacro(this->Reader, << "Error H5Dvlen_reclaim"); } H5Sclose(space); return array; } //------------------------------------------------------------------------------ vtkAbstractArray* vtkHDFReader::Implementation::NewFieldArray(const char* name) { hid_t dataset = -1; hid_t nativeType = -1; std::vector dims; if ((dataset = this->OpenDataSet( this->AttributeDataGroup[vtkDataObject::FIELD], name, &nativeType, dims)) < 0) { return nullptr; } TypeDescription td = GetTypeDescription(nativeType); if (td.Class == H5T_STRING) { vtkStringArray* array = nullptr; if (dims.size() == 1) { array = this->NewStringArray(dataset, dims[0]); } else { vtkErrorWithObjectMacro(this->Reader, << "Error: String array expected " "dimensions one but got: " << dims.size()); } H5Dclose(dataset); H5Tclose(nativeType); return array; } else { // empty fileExtent means read all values from the file // field arrays are always 1D std::vector fileExtent; return NewArray(this->AttributeDataGroup[vtkDataObject::FIELD], name, fileExtent); } } //------------------------------------------------------------------------------ vtkDataArray* vtkHDFReader::Implementation::NewMetadataArray( const char* name, hsize_t offset, hsize_t size) { std::vector fileExtent = { offset, offset + size - 1 }; return NewArray(this->VTKGroup, name, fileExtent); } //------------------------------------------------------------------------------ std::vector vtkHDFReader::Implementation::GetMetadata(const char* name, hsize_t size) { std::vector v; std::vector fileExtent = { 0, size - 1 }; auto a = vtk::TakeSmartPointer(NewArray(this->VTKGroup, name, fileExtent)); if (!a) { return v; } v.resize(a->GetNumberOfTuples()); auto range = vtk::DataArrayValueRange<1>(a); std::copy(range.begin(), range.end(), v.begin()); return v; } //------------------------------------------------------------------------------ vtkDataArray* vtkHDFReader::Implementation::NewArray( hid_t group, const char* name, const std::vector& parameterExtent) { hid_t dataset = -1; hid_t nativeType = -1; hsize_t numberOfComponents = 0; std::vector dims; std::vector extent = parameterExtent; if ((dataset = this->OpenDataSet(group, name, &nativeType, dims)) < 0) { return nullptr; } vtkDataArray* array = nullptr; try { // used for field arrays if (extent.empty()) { extent.resize(2, 0); extent[1] = dims[0] - 1; if (dims.size() > 2) { throw std::runtime_error("Field arrays cannot have more than 2 dimensions."); } } if (dims.size() < (extent.size() >> 1)) { std::ostringstream ostr; ostr << name << " dataset: Expecting ndims >= " << (extent.size() >> 1) << ", got: " << dims.size(); throw std::runtime_error(ostr.str()); } if (dims.size() == (extent.size() >> 1)) { numberOfComponents = 1; } else { numberOfComponents = dims[dims.size() - 1]; if (dims.size() > (extent.size() >> 1) + 1) { std::ostringstream ostr; ostr << name << " dataset: ndims: " << dims.size() << " greater than expected ndims: " << (extent.size() >> 1) << " plus one."; throw std::runtime_error(ostr.str()); } } auto it = this->TypeReaderMap.find(this->GetTypeDescription(nativeType)); if (it == this->TypeReaderMap.end()) { vtkErrorWithObjectMacro(this->Reader, "Unknown native datatype: " << nativeType); } else { array = (this->*(it->second))(dataset, extent, numberOfComponents); } } catch (const std::exception& e) { vtkErrorWithObjectMacro(this->Reader, << e.what()); } H5Dclose(dataset); H5Tclose(nativeType); return array; } //------------------------------------------------------------------------------ template vtkDataArray* vtkHDFReader::Implementation::NewArray( hid_t dataset, const std::vector& fileExtent, hsize_t numberOfComponents) { int numberOfTuples = 1; size_t ndims = fileExtent.size() >> 1; for (size_t i = 0; i < ndims; ++i) { size_t j = i << 1; numberOfTuples *= (fileExtent[j + 1] - fileExtent[j] + 1); } auto array = vtkAOSDataArrayTemplate::SafeDownCast(NewVtkDataArray()); array->SetNumberOfComponents(numberOfComponents); array->SetNumberOfTuples(numberOfTuples); T* data = array->GetPointer(0); if (!this->NewArray(dataset, fileExtent, numberOfComponents, data)) { array->Delete(); array = nullptr; } return array; } //------------------------------------------------------------------------------ template bool vtkHDFReader::Implementation::NewArray( hid_t dataset, const std::vector& fileExtent, hsize_t numberOfComponents, T* data) { hid_t nativeType = TemplateTypeToHdfNativeType(); hid_t memspace = -1; hid_t filespace = -1; bool error = false; try { // create the memory space, reverse axis order for VTK fortran order std::vector count(fileExtent.size() >> 1), start(fileExtent.size() >> 1); for (size_t i = 0; i < count.size(); ++i) { size_t j = (count.size() - 1 - i) << 1; count[i] = fileExtent[j + 1] - fileExtent[j] + 1; start[i] = fileExtent[j]; } if (numberOfComponents > 1) { count.push_back(numberOfComponents); start.push_back(0); } if ((memspace = H5Screate_simple(static_cast(count.size()), &count[0], nullptr)) < 0) { throw std::runtime_error("Error H5Screate_simple for memory space"); } // create the filespace and select the required fileExtent if ((filespace = H5Dget_space(dataset)) < 0) { throw std::runtime_error("Error H5Dget_space for imagedata"); } if (H5Sselect_hyperslab(filespace, H5S_SELECT_SET, &start[0], nullptr, &count[0], nullptr) < 0) { std::ostringstream ostr; std::ostream_iterator oi(ostr, " "); ostr << "Error selecting hyperslab, \nstart: "; std::copy(start.begin(), start.end(), oi); ostr << "\ncount: "; std::copy(count.begin(), count.end(), oi); throw std::runtime_error(ostr.str()); } // read hyperslab if (H5Dread(dataset, nativeType, memspace, filespace, H5P_DEFAULT, data) < 0) { std::ostringstream ostr; ostr << "Error H5Dread " << "start: " << start[0] << ", " << start[1] << ", " << start[2] << " count: " << count[0] << ", " << count[1] << ", " << count[2]; throw std::runtime_error(ostr.str()); } } catch (const std::exception& e) { vtkErrorWithObjectMacro(this->Reader, << e.what()); error = true; } if (memspace >= 0) { error = H5Sclose(memspace) < 0 || error; } if (filespace >= 0) { error = H5Sclose(filespace) < 0 || error; } return !error; } //------------------------------------------------------------------------------ // explicit template instantiation template bool vtkHDFReader::Implementation::GetAttribute( const char* attributeName, size_t dim, int* value); template bool vtkHDFReader::Implementation::GetAttribute( const char* attributeName, size_t dim, double* value);