From 0612be9c5bbbbdc49854ae2c1438d0d88c6de525 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Tue, 19 May 2020 12:27:01 -0600 Subject: [PATCH 1/5] Move VTKDataSetReaderBase code to vtkm_io Most of this code is not templated methods. Rather, it implements over several types to call templated functions, which creates quite a bit of code. Rather than have all code using a reader recompile the code, just compile it once and put it in a library. --- benchmarking/CMakeLists.txt | 2 +- examples/mesh_quality/CMakeLists.txt | 2 +- examples/particle_advection/CMakeLists.txt | 2 +- examples/redistribute_points/CMakeLists.txt | 2 +- examples/temporal_advection/CMakeLists.txt | 2 +- vtkm/filter/testing/CMakeLists.txt | 2 +- vtkm/io/CMakeLists.txt | 1 + vtkm/io/VTKDataSetReaderBase.cxx | 826 +++++++++++++++++++ vtkm/io/VTKDataSetReaderBase.h | 869 ++------------------ vtkm/io/VTKPolyDataReader.h | 1 + vtkm/io/testing/CMakeLists.txt | 16 +- 11 files changed, 912 insertions(+), 813 deletions(-) create mode 100644 vtkm/io/VTKDataSetReaderBase.cxx diff --git a/benchmarking/CMakeLists.txt b/benchmarking/CMakeLists.txt index a504d395e..bbfbd8c2a 100644 --- a/benchmarking/CMakeLists.txt +++ b/benchmarking/CMakeLists.txt @@ -52,7 +52,7 @@ set(VTKm_BENCHS_RANGE_UPPER_BOUNDARY 134217728 CACHE STRING "Biggest sample for mark_as_advanced(VTKm_BENCHS_RANGE_LOWER_BOUNDARY VTKm_BENCHS_RANGE_UPPER_BOUNDARY) foreach (benchmark ${benchmarks}) - add_benchmark(NAME ${benchmark} FILE ${benchmark}.cxx LIBS vtkm_source vtkm_filter) + add_benchmark(NAME ${benchmark} FILE ${benchmark}.cxx LIBS vtkm_source vtkm_filter vtkm_io) endforeach () target_compile_definitions(BenchmarkDeviceAdapter PUBLIC VTKm_BENCHS_RANGE_LOWER_BOUNDARY=${VTKm_BENCHS_RANGE_LOWER_BOUNDARY}) diff --git a/examples/mesh_quality/CMakeLists.txt b/examples/mesh_quality/CMakeLists.txt index 3987f3da2..6308b37b6 100644 --- a/examples/mesh_quality/CMakeLists.txt +++ b/examples/mesh_quality/CMakeLists.txt @@ -26,7 +26,7 @@ project(MeshQuality CXX) find_package(VTKm REQUIRED QUIET) add_executable(MeshQuality MeshQuality.cxx) -target_link_libraries(MeshQuality PRIVATE vtkm_filter) +target_link_libraries(MeshQuality PRIVATE vtkm_filter vtkm_io) if(TARGET vtkm::tbb) target_compile_definitions(MeshQuality PRIVATE BUILDING_TBB_VERSION) diff --git a/examples/particle_advection/CMakeLists.txt b/examples/particle_advection/CMakeLists.txt index 5c0b9a006..7866c56c6 100644 --- a/examples/particle_advection/CMakeLists.txt +++ b/examples/particle_advection/CMakeLists.txt @@ -14,7 +14,7 @@ project(ParticleAdvection CXX) find_package(VTKm REQUIRED QUIET) add_executable(Particle_Advection ParticleAdvection.cxx) -target_link_libraries(Particle_Advection PRIVATE vtkm_filter) +target_link_libraries(Particle_Advection PRIVATE vtkm_filter vtkm_io) vtkm_add_target_information(Particle_Advection DROP_UNUSED_SYMBOLS MODIFY_CUDA_FLAGS DEVICE_SOURCES ParticleAdvection.cxx) diff --git a/examples/redistribute_points/CMakeLists.txt b/examples/redistribute_points/CMakeLists.txt index 8a2ff67e9..59948d79f 100644 --- a/examples/redistribute_points/CMakeLists.txt +++ b/examples/redistribute_points/CMakeLists.txt @@ -13,7 +13,7 @@ project(RedistributePoints CXX) #Find the VTK-m package find_package(VTKm REQUIRED QUIET) add_executable(RedistributePoints RedistributePoints.cxx RedistributePoints.h) -target_link_libraries(RedistributePoints PRIVATE vtkm_filter) +target_link_libraries(RedistributePoints PRIVATE vtkm_filter vtkm_io) vtkm_add_target_information(RedistributePoints DROP_UNUSED_SYMBOLS MODIFY_CUDA_FLAGS DEVICE_SOURCES RedistributePoints.cxx) diff --git a/examples/temporal_advection/CMakeLists.txt b/examples/temporal_advection/CMakeLists.txt index 027a24f7a..eeed9ff44 100644 --- a/examples/temporal_advection/CMakeLists.txt +++ b/examples/temporal_advection/CMakeLists.txt @@ -19,4 +19,4 @@ add_executable(Temporal_Advection TemporalAdvection.cxx) vtkm_add_target_information(Temporal_Advection DROP_UNUSED_SYMBOLS MODIFY_CUDA_FLAGS DEVICE_SOURCES TemporalAdvection.cxx) -target_link_libraries(Temporal_Advection PRIVATE vtkm_filter) +target_link_libraries(Temporal_Advection PRIVATE vtkm_filter vtkm_io) diff --git a/vtkm/filter/testing/CMakeLists.txt b/vtkm/filter/testing/CMakeLists.txt index 72a572eeb..ab23e36be 100644 --- a/vtkm/filter/testing/CMakeLists.txt +++ b/vtkm/filter/testing/CMakeLists.txt @@ -69,7 +69,7 @@ set(unit_tests vtkm_unit_tests( SOURCES ${unit_tests} - LIBRARIES vtkm_filter vtkm_source + LIBRARIES vtkm_filter vtkm_source vtkm_io ALL_BACKENDS USE_VTKM_JOB_POOL ) diff --git a/vtkm/io/CMakeLists.txt b/vtkm/io/CMakeLists.txt index 7d8bc7660..32d9d9ae5 100644 --- a/vtkm/io/CMakeLists.txt +++ b/vtkm/io/CMakeLists.txt @@ -35,6 +35,7 @@ set(template_sources set(sources DecodePNG.cxx EncodePNG.cxx + VTKDataSetReaderBase.cxx ) vtkm_declare_headers( diff --git a/vtkm/io/VTKDataSetReaderBase.cxx b/vtkm/io/VTKDataSetReaderBase.cxx new file mode 100644 index 000000000..dcbace6e5 --- /dev/null +++ b/vtkm/io/VTKDataSetReaderBase.cxx @@ -0,0 +1,826 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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 + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace +{ + +inline void PrintVTKDataFileSummary(const vtkm::io::internal::VTKDataSetFile& df, std::ostream& out) +{ + out << "\tFile: " << df.FileName << std::endl; + out << "\tVersion: " << df.Version[0] << "." << df.Version[0] << std::endl; + out << "\tTitle: " << df.Title << std::endl; + out << "\tFormat: " << (df.IsBinary ? "BINARY" : "ASCII") << std::endl; + out << "\tDataSet type: " << vtkm::io::internal::DataSetStructureString(df.Structure) + << std::endl; +} + +// Since Fields and DataSets store data in the default VariantArrayHandle, convert +// the data to the closest type supported by default. The following will +// need to be updated if VariantArrayHandle or TypeListCommon changes. +template +struct ClosestCommonType +{ + using Type = T; +}; +template <> +struct ClosestCommonType +{ + using Type = vtkm::Int32; +}; +template <> +struct ClosestCommonType +{ + using Type = vtkm::Int32; +}; +template <> +struct ClosestCommonType +{ + using Type = vtkm::Int32; +}; +template <> +struct ClosestCommonType +{ + using Type = vtkm::Int32; +}; +template <> +struct ClosestCommonType +{ + using Type = vtkm::Int64; +}; +template <> +struct ClosestCommonType +{ + using Type = vtkm::Int64; +}; + +template +struct ClosestFloat +{ + using Type = T; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float32; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float32; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float32; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float32; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float64; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float64; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float64; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float64; +}; + +template +vtkm::cont::VariantArrayHandle CreateVariantArrayHandle(const std::vector& vec) +{ + switch (vtkm::VecTraits::NUM_COMPONENTS) + { + case 1: + { + using CommonType = typename ClosestCommonType::Type; + constexpr bool not_same = !std::is_same::value; + if (not_same) + { + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + "Type " << vtkm::io::internal::DataTypeName::Name() + << " is currently unsupported. Converting to " + << vtkm::io::internal::DataTypeName::Name() + << "."); + } + + vtkm::cont::ArrayHandle output; + output.Allocate(static_cast(vec.size())); + auto portal = output.WritePortal(); + for (vtkm::Id i = 0; i < output.GetNumberOfValues(); ++i) + { + portal.Set(i, static_cast(vec[static_cast(i)])); + } + + return vtkm::cont::VariantArrayHandle(output); + } + case 2: + case 3: + case 9: + { + constexpr auto numComps = vtkm::VecTraits::NUM_COMPONENTS; + + using InComponentType = typename vtkm::VecTraits::ComponentType; + using OutComponentType = typename ClosestFloat::Type; + using CommonType = vtkm::Vec; + constexpr bool not_same = !std::is_same::value; + if (not_same) + { + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + "Type " << vtkm::io::internal::DataTypeName::Name() << "[" + << vtkm::VecTraits::GetNumberOfComponents(T()) + << "] " + << "is currently unsupported. Converting to " + << vtkm::io::internal::DataTypeName::Name() + << "[" + << numComps + << "]."); + } + + vtkm::cont::ArrayHandle output; + output.Allocate(static_cast(vec.size())); + auto portal = output.WritePortal(); + for (vtkm::Id i = 0; i < output.GetNumberOfValues(); ++i) + { + CommonType outval = CommonType(); + for (vtkm::IdComponent j = 0; j < numComps; ++j) + { + outval[j] = static_cast( + vtkm::VecTraits::GetComponent(vec[static_cast(i)], j)); + } + portal.Set(i, outval); + } + + return vtkm::cont::VariantArrayHandle(output); + } + default: + { + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "Only 1, 2, 3, or 9 components supported. Skipping."); + return vtkm::cont::VariantArrayHandle(vtkm::cont::ArrayHandle()); + } + } +} + +} // anonymous namespace + +namespace vtkm +{ +namespace io +{ + +VTKDataSetReaderBase::VTKDataSetReaderBase(const char* fileName) + : DataFile(new internal::VTKDataSetFile) + , DataSet() + , Loaded(false) +{ + this->DataFile->FileName = fileName; +} + +VTKDataSetReaderBase::VTKDataSetReaderBase(const std::string& fileName) + : DataFile(new internal::VTKDataSetFile) + , DataSet() + , Loaded(false) +{ + this->DataFile->FileName = fileName; +} + +VTKDataSetReaderBase::~VTKDataSetReaderBase() +{ +} + +const vtkm::cont::DataSet& VTKDataSetReaderBase::ReadDataSet() +{ + if (!this->Loaded) + { + try + { + this->OpenFile(); + this->ReadHeader(); + this->Read(); + this->CloseFile(); + this->Loaded = true; + } + catch (std::ifstream::failure& e) + { + std::string message("IO Error: "); + throw vtkm::io::ErrorIO(message + e.what()); + } + } + + return this->DataSet; +} + +void VTKDataSetReaderBase::PrintSummary(std::ostream& out) const +{ + out << "VTKDataSetReader" << std::endl; + PrintVTKDataFileSummary(*this->DataFile.get(), out); + this->DataSet.PrintSummary(out); +} + +void VTKDataSetReaderBase::ReadPoints() +{ + std::string dataType; + std::size_t numPoints; + this->DataFile->Stream >> numPoints >> dataType >> std::ws; + + vtkm::cont::VariantArrayHandle points = + this->DoReadArrayVariant(vtkm::cont::Field::Association::POINTS, dataType, numPoints, 3); + + this->DataSet.AddCoordinateSystem(vtkm::cont::CoordinateSystem("coordinates", points)); +} + +void VTKDataSetReaderBase::ReadCells(vtkm::cont::ArrayHandle& connectivity, + vtkm::cont::ArrayHandle& numIndices) +{ + vtkm::Id numCells, numInts; + this->DataFile->Stream >> numCells >> numInts >> std::ws; + + connectivity.Allocate(numInts - numCells); + numIndices.Allocate(numCells); + + std::vector buffer(static_cast(numInts)); + this->ReadArray(buffer); + + vtkm::Int32* buffp = &buffer[0]; + auto connectivityPortal = connectivity.WritePortal(); + auto numIndicesPortal = numIndices.WritePortal(); + for (vtkm::Id i = 0, connInd = 0; i < numCells; ++i) + { + vtkm::IdComponent numInds = static_cast(*buffp++); + numIndicesPortal.Set(i, numInds); + for (vtkm::IdComponent j = 0; j < numInds; ++j, ++connInd) + { + connectivityPortal.Set(connInd, static_cast(*buffp++)); + } + } +} + +void VTKDataSetReaderBase::ReadShapes(vtkm::cont::ArrayHandle& shapes) +{ + std::string tag; + vtkm::Id numCells; + this->DataFile->Stream >> tag >> numCells >> std::ws; + internal::parseAssert(tag == "CELL_TYPES"); + + shapes.Allocate(numCells); + std::vector buffer(static_cast(numCells)); + this->ReadArray(buffer); + + vtkm::Int32* buffp = &buffer[0]; + auto shapesPortal = shapes.WritePortal(); + for (vtkm::Id i = 0; i < numCells; ++i) + { + shapesPortal.Set(i, static_cast(*buffp++)); + } +} + +void VTKDataSetReaderBase::ReadAttributes() +{ + if (this->DataFile->Stream.eof()) + { + return; + } + + vtkm::cont::Field::Association association = vtkm::cont::Field::Association::ANY; + std::size_t size; + + std::string tag; + this->DataFile->Stream >> tag; + while (!this->DataFile->Stream.eof()) + { + if (tag == "POINT_DATA") + { + association = vtkm::cont::Field::Association::POINTS; + } + else if (tag == "CELL_DATA") + { + association = vtkm::cont::Field::Association::CELL_SET; + } + else + { + internal::parseAssert(false); + } + + this->DataFile->Stream >> size; + while (!this->DataFile->Stream.eof()) + { + this->DataFile->Stream >> tag; + if (tag == "SCALARS") + { + this->ReadScalars(association, size); + } + else if (tag == "COLOR_SCALARS") + { + this->ReadColorScalars(association, size); + } + else if (tag == "LOOKUP_TABLE") + { + this->ReadLookupTable(); + } + else if (tag == "VECTORS" || tag == "NORMALS") + { + this->ReadVectors(association, size); + } + else if (tag == "TEXTURE_COORDINATES") + { + this->ReadTextureCoordinates(association, size); + } + else if (tag == "TENSORS") + { + this->ReadTensors(association, size); + } + else if (tag == "FIELD") + { + this->ReadFields(association, size); + } + else + { + break; + } + } + } +} + +void VTKDataSetReaderBase::CloseFile() +{ + this->DataFile->Stream.close(); +} + +void VTKDataSetReaderBase::OpenFile() +{ + this->DataFile->Stream.exceptions(std::ifstream::failbit | std::ifstream::badbit); + try + { + this->DataFile->Stream.open(this->DataFile->FileName.c_str(), + std::ios_base::in | std::ios_base::binary); + } + catch (std::ifstream::failure&) + { + std::string message("could not open file \"" + this->DataFile->FileName + "\""); + throw vtkm::io::ErrorIO(message); + } +} + +void VTKDataSetReaderBase::ReadHeader() +{ + char vstring[] = "# vtk DataFile Version"; + const std::size_t vlen = sizeof(vstring); + + // Read version line + char vbuf[vlen]; + this->DataFile->Stream.read(vbuf, vlen - 1); + vbuf[vlen - 1] = '\0'; + if (std::string(vbuf) != std::string(vstring)) + { + throw vtkm::io::ErrorIO("Incorrect file format."); + } + + char dot; + this->DataFile->Stream >> this->DataFile->Version[0] >> dot >> this->DataFile->Version[1]; + // skip rest of the line + std::string skip; + std::getline(this->DataFile->Stream, skip); + + if ((this->DataFile->Version[0] > 4) || + (this->DataFile->Version[0] == 4 && this->DataFile->Version[1] > 2)) + { + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, + "Reader may not correctly read >v4.2 files. Reading version " + << this->DataFile->Version[0] + << "." + << this->DataFile->Version[1] + << ".\n"); + } + + // Read title line + std::getline(this->DataFile->Stream, this->DataFile->Title); + + // Read format line + this->DataFile->IsBinary = false; + std::string format; + this->DataFile->Stream >> format >> std::ws; + if (format == "BINARY") + { + this->DataFile->IsBinary = true; + } + else if (format != "ASCII") + { + throw vtkm::io::ErrorIO("Unsupported Format."); + } + + // Read structure line + std::string tag, structStr; + this->DataFile->Stream >> tag >> structStr >> std::ws; + internal::parseAssert(tag == "DATASET"); + + this->DataFile->Structure = vtkm::io::internal::DataSetStructureId(structStr); + if (this->DataFile->Structure == vtkm::io::internal::DATASET_UNKNOWN) + { + throw vtkm::io::ErrorIO("Unsupported DataSet type."); + } +} + + +void VTKDataSetReaderBase::AddField(const std::string& name, + vtkm::cont::Field::Association association, + vtkm::cont::VariantArrayHandle& data) +{ + if (data.GetNumberOfValues() > 0) + { + switch (association) + { + case vtkm::cont::Field::Association::POINTS: + case vtkm::cont::Field::Association::WHOLE_MESH: + this->DataSet.AddField(vtkm::cont::Field(name, association, data)); + break; + case vtkm::cont::Field::Association::CELL_SET: + this->DataSet.AddField(vtkm::cont::Field(name, association, data)); + break; + default: + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, + "Not recording field '" << name << "' because it has an unknown association"); + break; + } + } +} + +void VTKDataSetReaderBase::ReadScalars(vtkm::cont::Field::Association association, + std::size_t numElements) +{ + std::string dataName, dataType, lookupTableName; + vtkm::IdComponent numComponents = 1; + this->DataFile->Stream >> dataName >> dataType; + std::string tag; + this->DataFile->Stream >> tag; + if (tag != "LOOKUP_TABLE") + { + try + { + numComponents = std::stoi(tag); + } + catch (std::invalid_argument&) + { + internal::parseAssert(false); + } + this->DataFile->Stream >> tag; + } + + internal::parseAssert(tag == "LOOKUP_TABLE"); + this->DataFile->Stream >> lookupTableName >> std::ws; + + vtkm::cont::VariantArrayHandle data = + this->DoReadArrayVariant(association, dataType, numElements, numComponents); + this->AddField(dataName, association, data); +} + +void VTKDataSetReaderBase::ReadColorScalars(vtkm::cont::Field::Association association, + std::size_t numElements) +{ + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "Support for COLOR_SCALARS is not implemented. Skipping."); + + std::string dataName; + vtkm::IdComponent numComponents; + this->DataFile->Stream >> dataName >> numComponents >> std::ws; + std::string dataType = this->DataFile->IsBinary ? "unsigned_char" : "float"; + vtkm::cont::VariantArrayHandle data = + this->DoReadArrayVariant(association, dataType, numElements, numComponents); + this->AddField(dataName, association, data); +} + +void VTKDataSetReaderBase::ReadLookupTable() +{ + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "Support for LOOKUP_TABLE is not implemented. Skipping."); + + std::string dataName; + std::size_t numEntries; + this->DataFile->Stream >> dataName >> numEntries >> std::ws; + this->SkipArray(numEntries, vtkm::Vec()); +} + +void VTKDataSetReaderBase::ReadTextureCoordinates(vtkm::cont::Field::Association association, + std::size_t numElements) +{ + std::string dataName; + vtkm::IdComponent numComponents; + std::string dataType; + this->DataFile->Stream >> dataName >> numComponents >> dataType >> std::ws; + + vtkm::cont::VariantArrayHandle data = + this->DoReadArrayVariant(association, dataType, numElements, numComponents); + this->AddField(dataName, association, data); +} + +void VTKDataSetReaderBase::ReadVectors(vtkm::cont::Field::Association association, + std::size_t numElements) +{ + std::string dataName; + std::string dataType; + this->DataFile->Stream >> dataName >> dataType >> std::ws; + + vtkm::cont::VariantArrayHandle data = + this->DoReadArrayVariant(association, dataType, numElements, 3); + this->AddField(dataName, association, data); +} + +void VTKDataSetReaderBase::ReadTensors(vtkm::cont::Field::Association association, + std::size_t numElements) +{ + std::string dataName; + std::string dataType; + this->DataFile->Stream >> dataName >> dataType >> std::ws; + + vtkm::cont::VariantArrayHandle data = + this->DoReadArrayVariant(association, dataType, numElements, 9); + this->AddField(dataName, association, data); +} + +void VTKDataSetReaderBase::ReadFields(vtkm::cont::Field::Association association, + std::size_t expectedNumElements) +{ + std::string dataName; + vtkm::Id numArrays; + this->DataFile->Stream >> dataName >> numArrays >> std::ws; + for (vtkm::Id i = 0; i < numArrays; ++i) + { + std::size_t numTuples; + vtkm::IdComponent numComponents; + std::string arrayName, dataType; + this->DataFile->Stream >> arrayName >> numComponents >> numTuples >> dataType >> std::ws; + if (numTuples == expectedNumElements) + { + vtkm::cont::VariantArrayHandle data = + this->DoReadArrayVariant(association, dataType, numTuples, numComponents); + this->AddField(arrayName, association, data); + } + else + { + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, + "Field " << arrayName + << "'s size does not match expected number of elements. Skipping"); + } + } +} + +void VTKDataSetReaderBase::ReadGlobalFields(std::vector* visitBounds) +{ + std::string dataName; + vtkm::Id numArrays; + this->DataFile->Stream >> dataName >> numArrays >> std::ws; + for (vtkm::Id i = 0; i < numArrays; ++i) + { + std::size_t numTuples; + vtkm::IdComponent numComponents; + std::string arrayName, dataType; + this->DataFile->Stream >> arrayName >> numComponents >> numTuples >> dataType >> std::ws; + if (arrayName == "avtOriginalBounds" && visitBounds) + { + visitBounds->resize(6); + internal::parseAssert(numComponents == 1 && numTuples == 6); + // parse the bounds and fill the bounds vector + this->ReadArray(*visitBounds); + } + else + { + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + "Support for global field " << arrayName << " not implemented. Skipping."); + this->DoSkipArrayVariant(dataType, numTuples, numComponents); + } + } +} + +class VTKDataSetReaderBase::SkipArrayVariant +{ +public: + SkipArrayVariant(VTKDataSetReaderBase* reader, std::size_t numElements) + : Reader(reader) + , NumElements(numElements) + { + } + + template + void operator()(T) const + { + this->Reader->SkipArray(this->NumElements, T()); + } + + template + void operator()(vtkm::IdComponent numComponents, T) const + { + this->Reader->SkipArray(this->NumElements * static_cast(numComponents), T()); + } + +protected: + VTKDataSetReaderBase* Reader; + std::size_t NumElements; +}; + +class VTKDataSetReaderBase::ReadArrayVariant : public SkipArrayVariant +{ +public: + ReadArrayVariant(VTKDataSetReaderBase* reader, + vtkm::cont::Field::Association association, + std::size_t numElements, + vtkm::cont::VariantArrayHandle& data) + : SkipArrayVariant(reader, numElements) + , Association(association) + , Data(&data) + { + } + + template + void operator()(T) const + { + std::vector buffer(this->NumElements); + this->Reader->ReadArray(buffer); + if ((this->Association != vtkm::cont::Field::Association::CELL_SET) || + (this->Reader->GetCellsPermutation().GetNumberOfValues() < 1)) + { + *this->Data = CreateVariantArrayHandle(buffer); + } + else + { + // If we are reading data associated with a cell set, we need to (sometimes) permute the + // data due to differences between VTK and VTK-m cell shapes. + auto permutation = this->Reader->GetCellsPermutation().ReadPortal(); + vtkm::Id outSize = permutation.GetNumberOfValues(); + std::vector permutedBuffer(static_cast(outSize)); + for (vtkm::Id outIndex = 0; outIndex < outSize; outIndex++) + { + std::size_t inIndex = static_cast(permutation.Get(outIndex)); + permutedBuffer[static_cast(outIndex)] = buffer[inIndex]; + } + *this->Data = CreateVariantArrayHandle(permutedBuffer); + } + } + + template + void operator()(vtkm::IdComponent numComponents, T) const + { + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, + "Support for " << numComponents << " components not implemented. Skipping."); + SkipArrayVariant::operator()(numComponents, T()); + } + +private: + vtkm::cont::Field::Association Association; + vtkm::cont::VariantArrayHandle* Data; +}; + +void VTKDataSetReaderBase::DoSkipArrayVariant(std::string dataType, + std::size_t numElements, + vtkm::IdComponent numComponents) +{ + // string is unsupported for SkipArrayVariant, so it requires some + // special handling + if (dataType == "string") + { + const vtkm::Id stringCount = numComponents * static_cast(numElements); + for (vtkm::Id i = 0; i < stringCount; ++i) + { + std::string trash; + this->DataFile->Stream >> trash; + } + } + else + { + vtkm::io::internal::DataType typeId = vtkm::io::internal::DataTypeId(dataType); + vtkm::io::internal::SelectTypeAndCall( + typeId, numComponents, SkipArrayVariant(this, numElements)); + } +} + +vtkm::cont::VariantArrayHandle VTKDataSetReaderBase::DoReadArrayVariant( + vtkm::cont::Field::Association association, + std::string dataType, + std::size_t numElements, + vtkm::IdComponent numComponents) +{ + // Create empty data to start so that the return can check if data were actually read + vtkm::cont::ArrayHandle empty; + vtkm::cont::VariantArrayHandle data(empty); + + vtkm::io::internal::DataType typeId = vtkm::io::internal::DataTypeId(dataType); + vtkm::io::internal::SelectTypeAndCall( + typeId, numComponents, ReadArrayVariant(this, association, numElements, data)); + + return data; +} + +void VTKDataSetReaderBase::ReadArray(std::vector& buffer) +{ + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, + "Support for data type 'bit' is not implemented. Skipping."); + this->SkipArray(buffer.size(), vtkm::io::internal::DummyBitType()); + buffer.clear(); +} + +void VTKDataSetReaderBase::SkipArray(std::size_t numElements, + vtkm::io::internal::DummyBitType, + vtkm::IdComponent numComponents) +{ + if (this->DataFile->IsBinary) + { + numElements = (numElements + 7) / 8; + this->DataFile->Stream.seekg(static_cast(numElements), std::ios_base::cur); + } + else + { + for (std::size_t i = 0; i < numElements; ++i) + { + vtkm::UInt16 val; + this->DataFile->Stream >> val; + } + } + this->DataFile->Stream >> std::ws; + this->SkipArrayMetaData(numComponents); +} + +void VTKDataSetReaderBase::SkipArrayMetaData(vtkm::IdComponent numComponents) +{ + if (!this->DataFile->Stream.good()) + { + return; + } + + auto begining = this->DataFile->Stream.tellg(); + + std::string tag; + this->DataFile->Stream >> tag; + if (tag != "METADATA") + { + this->DataFile->Stream.seekg(begining); + return; + } + + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "METADATA is not supported. Attempting to Skip."); + + this->DataFile->Stream >> tag >> std::ws; + if (tag == "COMPONENT_NAMES") + { + std::string name; + for (vtkm::IdComponent i = 0; i < numComponents; ++i) + { + this->DataFile->Stream >> name >> std::ws; + } + } + else if (tag == "INFORMATION") + { + int numKeys = 0; + this->DataFile->Stream >> numKeys >> std::ws; + + // Skipping INFORMATION is tricky. The reader needs to be aware of the types of the + // information, which is not provided in the file. + // Here we will just skip until an empty line is found. + // However, if there are no keys, then there is nothing to read (and the stream tends + // to skip over empty lines. + if (numKeys > 0) + { + std::string line; + do + { + std::getline(this->DataFile->Stream, line); + } while (this->DataFile->Stream.good() && !line.empty()); + + // Eat any remaining whitespace after the INFORMATION to be ready to read the next token + this->DataFile->Stream >> std::ws; + } + } + else + { + internal::parseAssert(false); + } +} +} +} // namespace vtkm::io diff --git a/vtkm/io/VTKDataSetReaderBase.h b/vtkm/io/VTKDataSetReaderBase.h index 70292392c..62a935315 100644 --- a/vtkm/io/VTKDataSetReaderBase.h +++ b/vtkm/io/VTKDataSetReaderBase.h @@ -10,25 +10,16 @@ #ifndef vtk_m_io_VTKDataSetReaderBase_h #define vtk_m_io_VTKDataSetReaderBase_h +#include +#include +#include +#include + #include -#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include #include -#include -#include namespace vtkm { @@ -48,16 +39,6 @@ struct VTKDataSetFile std::ifstream Stream; }; -inline void PrintVTKDataFileSummary(const VTKDataSetFile& df, std::ostream& out) -{ - out << "\tFile: " << df.FileName << std::endl; - out << "\tVersion: " << df.Version[0] << "." << df.Version[0] << std::endl; - out << "\tTitle: " << df.Title << std::endl; - out << "\tFormat: " << (df.IsBinary ? "BINARY" : "ASCII") << std::endl; - out << "\tDataSet type: " << vtkm::io::internal::DataSetStructureString(df.Structure) - << std::endl; -} - inline void parseAssert(bool condition) { if (!condition) @@ -82,166 +63,6 @@ struct StreamIOType using Type = vtkm::UInt16; }; -// Since Fields and DataSets store data in the default VariantArrayHandle, convert -// the data to the closest type supported by default. The following will -// need to be updated if VariantArrayHandle or TypeListCommon changes. -template -struct ClosestCommonType -{ - using Type = T; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int32; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int32; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int32; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int32; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int64; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int64; -}; - -template -struct ClosestFloat -{ - using Type = T; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float32; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float32; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float32; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float32; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float64; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float64; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float64; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float64; -}; - -template -vtkm::cont::VariantArrayHandle CreateVariantArrayHandle(const std::vector& vec) -{ - switch (vtkm::VecTraits::NUM_COMPONENTS) - { - case 1: - { - using CommonType = typename ClosestCommonType::Type; - constexpr bool not_same = !std::is_same::value; - if (not_same) - { - VTKM_LOG_S(vtkm::cont::LogLevel::Info, - "Type " << vtkm::io::internal::DataTypeName::Name() - << " is currently unsupported. Converting to " - << vtkm::io::internal::DataTypeName::Name() - << "."); - } - - vtkm::cont::ArrayHandle output; - output.Allocate(static_cast(vec.size())); - auto portal = output.WritePortal(); - for (vtkm::Id i = 0; i < output.GetNumberOfValues(); ++i) - { - portal.Set(i, static_cast(vec[static_cast(i)])); - } - - return vtkm::cont::VariantArrayHandle(output); - } - case 2: - case 3: - case 9: - { - constexpr auto numComps = vtkm::VecTraits::NUM_COMPONENTS; - - using InComponentType = typename vtkm::VecTraits::ComponentType; - using OutComponentType = typename ClosestFloat::Type; - using CommonType = vtkm::Vec; - constexpr bool not_same = !std::is_same::value; - if (not_same) - { - VTKM_LOG_S(vtkm::cont::LogLevel::Info, - "Type " << vtkm::io::internal::DataTypeName::Name() << "[" - << vtkm::VecTraits::GetNumberOfComponents(T()) - << "] " - << "is currently unsupported. Converting to " - << vtkm::io::internal::DataTypeName::Name() - << "[" - << numComps - << "]."); - } - - vtkm::cont::ArrayHandle output; - output.Allocate(static_cast(vec.size())); - auto portal = output.WritePortal(); - for (vtkm::Id i = 0; i < output.GetNumberOfValues(); ++i) - { - CommonType outval = CommonType(); - for (vtkm::IdComponent j = 0; j < numComps; ++j) - { - outval[j] = static_cast( - vtkm::VecTraits::GetComponent(vec[static_cast(i)], j)); - } - portal.Set(i, outval); - } - - return vtkm::cont::VariantArrayHandle(output); - } - default: - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "Only 1, 2, 3, or 9 components supported. Skipping."); - return vtkm::cont::VariantArrayHandle(vtkm::cont::ArrayHandle()); - } - } -} - inline vtkm::cont::DynamicCellSet CreateCellSetStructured(const vtkm::Id3& dim) { if (dim[0] > 1 && dim[1] > 1 && dim[2] > 1) @@ -272,564 +93,102 @@ inline vtkm::cont::DynamicCellSet CreateCellSetStructured(const vtkm::Id3& dim) } // namespace internal -VTKM_SILENCE_WEAK_VTABLE_WARNING_START - -class VTKDataSetReaderBase +class VTKM_IO_EXPORT VTKDataSetReaderBase { +protected: + std::unique_ptr DataFile; + vtkm::cont::DataSet DataSet; + +private: + bool Loaded; + vtkm::cont::ArrayHandle CellsPermutation; + + friend class VTKDataSetReader; + public: - explicit VTKDataSetReaderBase(const char* fileName) - : DataFile(new internal::VTKDataSetFile) - , DataSet() - , Loaded(false) - { - this->DataFile->FileName = fileName; - } + explicit VTKM_CONT VTKDataSetReaderBase(const char* fileName); - explicit VTKDataSetReaderBase(const std::string& fileName) - : DataFile(new internal::VTKDataSetFile) - , DataSet() - , Loaded(false) - { - this->DataFile->FileName = fileName; - } + explicit VTKM_CONT VTKDataSetReaderBase(const std::string& fileName); - virtual ~VTKDataSetReaderBase() {} + virtual VTKM_CONT ~VTKDataSetReaderBase(); - const vtkm::cont::DataSet& ReadDataSet() - { - if (!this->Loaded) - { - try - { - this->OpenFile(); - this->ReadHeader(); - this->Read(); - this->CloseFile(); - this->Loaded = true; - } - catch (std::ifstream::failure& e) - { - std::string message("IO Error: "); - throw vtkm::io::ErrorIO(message + e.what()); - } - } + VTKDataSetReaderBase(const VTKDataSetReaderBase&) = delete; + void operator=(const VTKDataSetReaderBase&) = delete; - return this->DataSet; - } + const VTKM_CONT vtkm::cont::DataSet& ReadDataSet(); const vtkm::cont::DataSet& GetDataSet() const { return this->DataSet; } - virtual void PrintSummary(std::ostream& out) const - { - out << "VTKDataSetReader" << std::endl; - PrintVTKDataFileSummary(*this->DataFile.get(), out); - this->DataSet.PrintSummary(out); - } + virtual VTKM_CONT void PrintSummary(std::ostream& out) const; protected: - void ReadPoints() - { - std::string dataType; - std::size_t numPoints; - this->DataFile->Stream >> numPoints >> dataType >> std::ws; + VTKM_CONT void ReadPoints(); - vtkm::cont::VariantArrayHandle points = - this->DoReadArrayVariant(vtkm::cont::Field::Association::POINTS, dataType, numPoints, 3); + VTKM_CONT void ReadCells(vtkm::cont::ArrayHandle& connectivity, + vtkm::cont::ArrayHandle& numIndices); - this->DataSet.AddCoordinateSystem(vtkm::cont::CoordinateSystem("coordinates", points)); - } + VTKM_CONT void ReadShapes(vtkm::cont::ArrayHandle& shapes); - void ReadCells(vtkm::cont::ArrayHandle& connectivity, - vtkm::cont::ArrayHandle& numIndices) - { - vtkm::Id numCells, numInts; - this->DataFile->Stream >> numCells >> numInts >> std::ws; - - connectivity.Allocate(numInts - numCells); - numIndices.Allocate(numCells); - - std::vector buffer(static_cast(numInts)); - this->ReadArray(buffer); - - vtkm::Int32* buffp = &buffer[0]; - auto connectivityPortal = connectivity.WritePortal(); - auto numIndicesPortal = numIndices.WritePortal(); - for (vtkm::Id i = 0, connInd = 0; i < numCells; ++i) - { - vtkm::IdComponent numInds = static_cast(*buffp++); - numIndicesPortal.Set(i, numInds); - for (vtkm::IdComponent j = 0; j < numInds; ++j, ++connInd) - { - connectivityPortal.Set(connInd, static_cast(*buffp++)); - } - } - } - - void ReadShapes(vtkm::cont::ArrayHandle& shapes) - { - std::string tag; - vtkm::Id numCells; - this->DataFile->Stream >> tag >> numCells >> std::ws; - internal::parseAssert(tag == "CELL_TYPES"); - - shapes.Allocate(numCells); - std::vector buffer(static_cast(numCells)); - this->ReadArray(buffer); - - vtkm::Int32* buffp = &buffer[0]; - auto shapesPortal = shapes.WritePortal(); - for (vtkm::Id i = 0; i < numCells; ++i) - { - shapesPortal.Set(i, static_cast(*buffp++)); - } - } - - void ReadAttributes() - { - if (this->DataFile->Stream.eof()) - { - return; - } - - vtkm::cont::Field::Association association = vtkm::cont::Field::Association::ANY; - std::size_t size; - - std::string tag; - this->DataFile->Stream >> tag; - while (!this->DataFile->Stream.eof()) - { - if (tag == "POINT_DATA") - { - association = vtkm::cont::Field::Association::POINTS; - } - else if (tag == "CELL_DATA") - { - association = vtkm::cont::Field::Association::CELL_SET; - } - else - { - internal::parseAssert(false); - } - - this->DataFile->Stream >> size; - while (!this->DataFile->Stream.eof()) - { - this->DataFile->Stream >> tag; - if (tag == "SCALARS") - { - this->ReadScalars(association, size); - } - else if (tag == "COLOR_SCALARS") - { - this->ReadColorScalars(association, size); - } - else if (tag == "LOOKUP_TABLE") - { - this->ReadLookupTable(); - } - else if (tag == "VECTORS" || tag == "NORMALS") - { - this->ReadVectors(association, size); - } - else if (tag == "TEXTURE_COORDINATES") - { - this->ReadTextureCoordinates(association, size); - } - else if (tag == "TENSORS") - { - this->ReadTensors(association, size); - } - else if (tag == "FIELD") - { - this->ReadFields(association, size); - } - else - { - break; - } - } - } - } + VTKM_CONT void ReadAttributes(); void SetCellsPermutation(const vtkm::cont::ArrayHandle& permutation) { this->CellsPermutation = permutation; } - vtkm::cont::ArrayHandle GetCellsPermutation() const { return this->CellsPermutation; } + VTKM_CONT vtkm::cont::ArrayHandle GetCellsPermutation() const + { + return this->CellsPermutation; + } - void TransferDataFile(VTKDataSetReaderBase& reader) + VTKM_CONT void TransferDataFile(VTKDataSetReaderBase& reader) { reader.DataFile.swap(this->DataFile); this->DataFile.reset(nullptr); } - virtual void CloseFile() { this->DataFile->Stream.close(); } + VTKM_CONT virtual void CloseFile(); + + VTKM_CONT virtual void Read() = 0; private: - void OpenFile() - { - this->DataFile->Stream.exceptions(std::ifstream::failbit | std::ifstream::badbit); - try - { - this->DataFile->Stream.open(this->DataFile->FileName.c_str(), - std::ios_base::in | std::ios_base::binary); - } - catch (std::ifstream::failure&) - { - std::string message("could not open file \"" + this->DataFile->FileName + "\""); - throw vtkm::io::ErrorIO(message); - } - } - - void ReadHeader() - { - char vstring[] = "# vtk DataFile Version"; - const std::size_t vlen = sizeof(vstring); - - // Read version line - char vbuf[vlen]; - this->DataFile->Stream.read(vbuf, vlen - 1); - vbuf[vlen - 1] = '\0'; - if (std::string(vbuf) != std::string(vstring)) - { - throw vtkm::io::ErrorIO("Incorrect file format."); - } - - char dot; - this->DataFile->Stream >> this->DataFile->Version[0] >> dot >> this->DataFile->Version[1]; - // skip rest of the line - std::string skip; - std::getline(this->DataFile->Stream, skip); - - if ((this->DataFile->Version[0] > 4) || - (this->DataFile->Version[0] == 4 && this->DataFile->Version[1] > 2)) - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Reader may not correctly read >v4.2 files. Reading version " - << this->DataFile->Version[0] - << "." - << this->DataFile->Version[1] - << ".\n"); - } - - // Read title line - std::getline(this->DataFile->Stream, this->DataFile->Title); - - // Read format line - this->DataFile->IsBinary = false; - std::string format; - this->DataFile->Stream >> format >> std::ws; - if (format == "BINARY") - { - this->DataFile->IsBinary = true; - } - else if (format != "ASCII") - { - throw vtkm::io::ErrorIO("Unsupported Format."); - } - - // Read structure line - std::string tag, structStr; - this->DataFile->Stream >> tag >> structStr >> std::ws; - internal::parseAssert(tag == "DATASET"); - - this->DataFile->Structure = vtkm::io::internal::DataSetStructureId(structStr); - if (this->DataFile->Structure == vtkm::io::internal::DATASET_UNKNOWN) - { - throw vtkm::io::ErrorIO("Unsupported DataSet type."); - } - } - - virtual void Read() = 0; - - void AddField(const std::string& name, - vtkm::cont::Field::Association association, - vtkm::cont::VariantArrayHandle& data) - { - if (data.GetNumberOfValues() > 0) - { - switch (association) - { - case vtkm::cont::Field::Association::POINTS: - case vtkm::cont::Field::Association::WHOLE_MESH: - this->DataSet.AddField(vtkm::cont::Field(name, association, data)); - break; - case vtkm::cont::Field::Association::CELL_SET: - this->DataSet.AddField(vtkm::cont::Field(name, association, data)); - break; - default: - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Not recording field '" << name << "' because it has an unknown association"); - break; - } - } - } - - void ReadScalars(vtkm::cont::Field::Association association, std::size_t numElements) - { - std::string dataName, dataType, lookupTableName; - vtkm::IdComponent numComponents = 1; - this->DataFile->Stream >> dataName >> dataType; - std::string tag; - this->DataFile->Stream >> tag; - if (tag != "LOOKUP_TABLE") - { - try - { - numComponents = std::stoi(tag); - } - catch (std::invalid_argument&) - { - internal::parseAssert(false); - } - this->DataFile->Stream >> tag; - } - - internal::parseAssert(tag == "LOOKUP_TABLE"); - this->DataFile->Stream >> lookupTableName >> std::ws; - - vtkm::cont::VariantArrayHandle data = - this->DoReadArrayVariant(association, dataType, numElements, numComponents); - this->AddField(dataName, association, data); - } - - void ReadColorScalars(vtkm::cont::Field::Association association, std::size_t numElements) - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Support for COLOR_SCALARS is not implemented. Skipping."); - - std::string dataName; - vtkm::IdComponent numComponents; - this->DataFile->Stream >> dataName >> numComponents >> std::ws; - std::string dataType = this->DataFile->IsBinary ? "unsigned_char" : "float"; - vtkm::cont::VariantArrayHandle data = - this->DoReadArrayVariant(association, dataType, numElements, numComponents); - this->AddField(dataName, association, data); - } - - void ReadLookupTable() - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Support for LOOKUP_TABLE is not implemented. Skipping."); - - std::string dataName; - std::size_t numEntries; - this->DataFile->Stream >> dataName >> numEntries >> std::ws; - this->SkipArray(numEntries, vtkm::Vec()); - } - - void ReadTextureCoordinates(vtkm::cont::Field::Association association, std::size_t numElements) - { - std::string dataName; - vtkm::IdComponent numComponents; - std::string dataType; - this->DataFile->Stream >> dataName >> numComponents >> dataType >> std::ws; - - vtkm::cont::VariantArrayHandle data = - this->DoReadArrayVariant(association, dataType, numElements, numComponents); - this->AddField(dataName, association, data); - } - - void ReadVectors(vtkm::cont::Field::Association association, std::size_t numElements) - { - std::string dataName; - std::string dataType; - this->DataFile->Stream >> dataName >> dataType >> std::ws; - - vtkm::cont::VariantArrayHandle data = - this->DoReadArrayVariant(association, dataType, numElements, 3); - this->AddField(dataName, association, data); - } - - void ReadTensors(vtkm::cont::Field::Association association, std::size_t numElements) - { - std::string dataName; - std::string dataType; - this->DataFile->Stream >> dataName >> dataType >> std::ws; - - vtkm::cont::VariantArrayHandle data = - this->DoReadArrayVariant(association, dataType, numElements, 9); - this->AddField(dataName, association, data); - } - - void ReadFields(vtkm::cont::Field::Association association, std::size_t expectedNumElements) - { - std::string dataName; - vtkm::Id numArrays; - this->DataFile->Stream >> dataName >> numArrays >> std::ws; - for (vtkm::Id i = 0; i < numArrays; ++i) - { - std::size_t numTuples; - vtkm::IdComponent numComponents; - std::string arrayName, dataType; - this->DataFile->Stream >> arrayName >> numComponents >> numTuples >> dataType >> std::ws; - if (numTuples == expectedNumElements) - { - vtkm::cont::VariantArrayHandle data = - this->DoReadArrayVariant(association, dataType, numTuples, numComponents); - this->AddField(arrayName, association, data); - } - else - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Field " << arrayName - << "'s size does not match expected number of elements. Skipping"); - } - } - } + VTKM_CONT void OpenFile(); + VTKM_CONT void ReadHeader(); + VTKM_CONT void AddField(const std::string& name, + vtkm::cont::Field::Association association, + vtkm::cont::VariantArrayHandle& data); + VTKM_CONT void ReadScalars(vtkm::cont::Field::Association association, std::size_t numElements); + VTKM_CONT void ReadColorScalars(vtkm::cont::Field::Association association, + std::size_t numElements); + VTKM_CONT void ReadLookupTable(); + VTKM_CONT void ReadTextureCoordinates(vtkm::cont::Field::Association association, + std::size_t numElements); + VTKM_CONT void ReadVectors(vtkm::cont::Field::Association association, std::size_t numElements); + VTKM_CONT void ReadTensors(vtkm::cont::Field::Association association, std::size_t numElements); + VTKM_CONT void ReadFields(vtkm::cont::Field::Association association, + std::size_t expectedNumElements); protected: - void ReadGlobalFields(std::vector* visitBounds = nullptr) - { - std::string dataName; - vtkm::Id numArrays; - this->DataFile->Stream >> dataName >> numArrays >> std::ws; - for (vtkm::Id i = 0; i < numArrays; ++i) - { - std::size_t numTuples; - vtkm::IdComponent numComponents; - std::string arrayName, dataType; - this->DataFile->Stream >> arrayName >> numComponents >> numTuples >> dataType >> std::ws; - if (arrayName == "avtOriginalBounds" && visitBounds) - { - visitBounds->resize(6); - internal::parseAssert(numComponents == 1 && numTuples == 6); - // parse the bounds and fill the bounds vector - this->ReadArray(*visitBounds); - } - else - { - VTKM_LOG_S(vtkm::cont::LogLevel::Info, - "Support for global field " << arrayName << " not implemented. Skipping."); - this->DoSkipArrayVariant(dataType, numTuples, numComponents); - } - } - } + VTKM_CONT void ReadGlobalFields(std::vector* visitBounds = nullptr); private: - class SkipArrayVariant - { - public: - SkipArrayVariant(VTKDataSetReaderBase* reader, std::size_t numElements) - : Reader(reader) - , NumElements(numElements) - { - } - - template - void operator()(T) const - { - this->Reader->SkipArray(this->NumElements, T()); - } - - template - void operator()(vtkm::IdComponent numComponents, T) const - { - this->Reader->SkipArray(this->NumElements * static_cast(numComponents), T()); - } - - protected: - VTKDataSetReaderBase* Reader; - std::size_t NumElements; - }; - - class ReadArrayVariant : public SkipArrayVariant - { - public: - ReadArrayVariant(VTKDataSetReaderBase* reader, - vtkm::cont::Field::Association association, - std::size_t numElements, - vtkm::cont::VariantArrayHandle& data) - : SkipArrayVariant(reader, numElements) - , Association(association) - , Data(&data) - { - } - - template - void operator()(T) const - { - std::vector buffer(this->NumElements); - this->Reader->ReadArray(buffer); - if ((this->Association != vtkm::cont::Field::Association::CELL_SET) || - (this->Reader->GetCellsPermutation().GetNumberOfValues() < 1)) - { - *this->Data = internal::CreateVariantArrayHandle(buffer); - } - else - { - // If we are reading data associated with a cell set, we need to (sometimes) permute the - // data due to differences between VTK and VTK-m cell shapes. - auto permutation = this->Reader->GetCellsPermutation().ReadPortal(); - vtkm::Id outSize = permutation.GetNumberOfValues(); - std::vector permutedBuffer(static_cast(outSize)); - for (vtkm::Id outIndex = 0; outIndex < outSize; outIndex++) - { - std::size_t inIndex = static_cast(permutation.Get(outIndex)); - permutedBuffer[static_cast(outIndex)] = buffer[inIndex]; - } - *this->Data = internal::CreateVariantArrayHandle(permutedBuffer); - } - } - - template - void operator()(vtkm::IdComponent numComponents, T) const - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Support for " << numComponents << " components not implemented. Skipping."); - SkipArrayVariant::operator()(numComponents, T()); - } - - private: - vtkm::cont::Field::Association Association; - vtkm::cont::VariantArrayHandle* Data; - }; + class SkipArrayVariant; + class ReadArrayVariant; //Make the Array parsing methods protected so that derived classes //can call the methods. protected: - void DoSkipArrayVariant(std::string dataType, - std::size_t numElements, - vtkm::IdComponent numComponents) - { - // string is unsupported for SkipArrayVariant, so it requires some - // special handling - if (dataType == "string") - { - const vtkm::Id stringCount = numComponents * static_cast(numElements); - for (vtkm::Id i = 0; i < stringCount; ++i) - { - std::string trash; - this->DataFile->Stream >> trash; - } - } - else - { - vtkm::io::internal::DataType typeId = vtkm::io::internal::DataTypeId(dataType); - vtkm::io::internal::SelectTypeAndCall( - typeId, numComponents, SkipArrayVariant(this, numElements)); - } - } - - vtkm::cont::VariantArrayHandle DoReadArrayVariant(vtkm::cont::Field::Association association, - std::string dataType, - std::size_t numElements, - vtkm::IdComponent numComponents) - { - // Create empty data to start so that the return can check if data were actually read - vtkm::cont::ArrayHandle empty; - vtkm::cont::VariantArrayHandle data(empty); - - vtkm::io::internal::DataType typeId = vtkm::io::internal::DataTypeId(dataType); - vtkm::io::internal::SelectTypeAndCall( - typeId, numComponents, ReadArrayVariant(this, association, numElements, data)); - - return data; - } + VTKM_CONT void DoSkipArrayVariant(std::string dataType, + std::size_t numElements, + vtkm::IdComponent numComponents); + VTKM_CONT vtkm::cont::VariantArrayHandle DoReadArrayVariant( + vtkm::cont::Field::Association association, + std::string dataType, + std::size_t numElements, + vtkm::IdComponent numComponents); template - void ReadArray(std::vector& buffer) + VTKM_CONT void ReadArray(std::vector& buffer) { using ComponentType = typename vtkm::VecTraits::ComponentType; constexpr vtkm::IdComponent numComponents = vtkm::VecTraits::NUM_COMPONENTS; @@ -861,7 +220,8 @@ protected: } template - void ReadArray(std::vector>& buffer) + VTKM_CONT void ReadArray( + std::vector>& buffer) { VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "Support for data type 'bit' is not implemented. Skipping."); @@ -869,13 +229,7 @@ protected: buffer.clear(); } - void ReadArray(std::vector& buffer) - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Support for data type 'bit' is not implemented. Skipping."); - this->SkipArray(buffer.size(), vtkm::io::internal::DummyBitType()); - buffer.clear(); - } + VTKM_CONT void ReadArray(std::vector& buffer); template void SkipArray(std::size_t numElements, T) @@ -912,95 +266,12 @@ protected: NumComponents); } - void SkipArray(std::size_t numElements, - vtkm::io::internal::DummyBitType, - vtkm::IdComponent numComponents = 1) - { - if (this->DataFile->IsBinary) - { - numElements = (numElements + 7) / 8; - this->DataFile->Stream.seekg(static_cast(numElements), std::ios_base::cur); - } - else - { - for (std::size_t i = 0; i < numElements; ++i) - { - vtkm::UInt16 val; - this->DataFile->Stream >> val; - } - } - this->DataFile->Stream >> std::ws; - this->SkipArrayMetaData(numComponents); - } + VTKM_CONT void SkipArray(std::size_t numElements, + vtkm::io::internal::DummyBitType, + vtkm::IdComponent numComponents = 1); - void SkipArrayMetaData(vtkm::IdComponent numComponents) - { - if (!this->DataFile->Stream.good()) - { - return; - } - - auto begining = this->DataFile->Stream.tellg(); - - std::string tag; - this->DataFile->Stream >> tag; - if (tag != "METADATA") - { - this->DataFile->Stream.seekg(begining); - return; - } - - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "METADATA is not supported. Attempting to Skip."); - - this->DataFile->Stream >> tag >> std::ws; - if (tag == "COMPONENT_NAMES") - { - std::string name; - for (vtkm::IdComponent i = 0; i < numComponents; ++i) - { - this->DataFile->Stream >> name >> std::ws; - } - } - else if (tag == "INFORMATION") - { - int numKeys = 0; - this->DataFile->Stream >> numKeys >> std::ws; - - // Skipping INFORMATION is tricky. The reader needs to be aware of the types of the - // information, which is not provided in the file. - // Here we will just skip until an empty line is found. - // However, if there are no keys, then there is nothing to read (and the stream tends - // to skip over empty lines. - if (numKeys > 0) - { - std::string line; - do - { - std::getline(this->DataFile->Stream, line); - } while (this->DataFile->Stream.good() && !line.empty()); - - // Eat any remaining whitespace after the INFORMATION to be ready to read the next token - this->DataFile->Stream >> std::ws; - } - } - else - { - internal::parseAssert(false); - } - } - -protected: - std::unique_ptr DataFile; - vtkm::cont::DataSet DataSet; - -private: - bool Loaded; - vtkm::cont::ArrayHandle CellsPermutation; - - friend class VTKDataSetReader; + VTKM_CONT void SkipArrayMetaData(vtkm::IdComponent numComponents); }; - -VTKM_SILENCE_WEAK_VTABLE_WARNING_END } } // vtkm::io diff --git a/vtkm/io/VTKPolyDataReader.h b/vtkm/io/VTKPolyDataReader.h index 3629f62ca..dcc0623f7 100644 --- a/vtkm/io/VTKPolyDataReader.h +++ b/vtkm/io/VTKPolyDataReader.h @@ -11,6 +11,7 @@ #define vtk_m_io_VTKPolyDataReader_h #include +#include #include diff --git a/vtkm/io/testing/CMakeLists.txt b/vtkm/io/testing/CMakeLists.txt index f17628c77..33ee8762e 100644 --- a/vtkm/io/testing/CMakeLists.txt +++ b/vtkm/io/testing/CMakeLists.txt @@ -15,14 +15,14 @@ set(unit_tests UnitTestVTKDataSetWriter.cxx ) -vtkm_unit_tests(SOURCES ${unit_tests} ALL_BACKENDS LIBRARIES vtkm_lodepng) +set(unit_test_libraries vtkm_lodepng vtkm_io) -if(NOT VTKm_ENABLE_RENDERING) - return() +if(VTKm_ENABLE_RENDERING) + set(unit_tests ${unit_tests} + UnitTestImageWriter.cxx + ) + + set(unit_test_libraries ${unit_test_libraries} vtkm_rendering) endif() -set(image_unit_tests - UnitTestImageWriter.cxx -) - -vtkm_unit_tests(NAME UnitTests_vtkm_io_image_testing SOURCES ${image_unit_tests} ALL_BACKENDS LIBRARIES vtkm_rendering vtkm_lodepng) +vtkm_unit_tests(SOURCES ${unit_tests} ALL_BACKENDS LIBRARIES ${unit_test_libraries}) From 086439e71ec66f04b04c86eab73bb9fccc632fd9 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Tue, 19 May 2020 13:04:12 -0600 Subject: [PATCH 2/5] Update variants of VTKDataSetReaders to compile into vtkm_io --- docs/changelog/vtk-io-in-library.md | 12 ++ vtkm/io/CMakeLists.txt | 6 + vtkm/io/VTKDataSetReader.cxx | 92 ++++++++++++++++ vtkm/io/VTKDataSetReader.h | 77 ++----------- vtkm/io/VTKPolyDataReader.cxx | 152 ++++++++++++++++++++++++++ vtkm/io/VTKPolyDataReader.h | 140 +----------------------- vtkm/io/VTKRectilinearGridReader.cxx | 95 ++++++++++++++++ vtkm/io/VTKRectilinearGridReader.h | 82 +------------- vtkm/io/VTKStructuredGridReader.cxx | 62 +++++++++++ vtkm/io/VTKStructuredGridReader.h | 46 +------- vtkm/io/VTKStructuredPointsReader.cxx | 79 +++++++++++++ vtkm/io/VTKStructuredPointsReader.h | 63 +---------- vtkm/io/VTKUnstructuredGridReader.cxx | 88 +++++++++++++++ vtkm/io/VTKUnstructuredGridReader.h | 70 +----------- 14 files changed, 615 insertions(+), 449 deletions(-) create mode 100644 docs/changelog/vtk-io-in-library.md create mode 100644 vtkm/io/VTKDataSetReader.cxx create mode 100644 vtkm/io/VTKPolyDataReader.cxx create mode 100644 vtkm/io/VTKRectilinearGridReader.cxx create mode 100644 vtkm/io/VTKStructuredGridReader.cxx create mode 100644 vtkm/io/VTKStructuredPointsReader.cxx create mode 100644 vtkm/io/VTKUnstructuredGridReader.cxx diff --git a/docs/changelog/vtk-io-in-library.md b/docs/changelog/vtk-io-in-library.md new file mode 100644 index 000000000..d6616813e --- /dev/null +++ b/docs/changelog/vtk-io-in-library.md @@ -0,0 +1,12 @@ +# Move VTK file readers and writers into vtkm_io + +The legacy VTK file reader and writer were created back when VTK-m was a +header-only library. Things have changed and we now compile quite a bit of +code into libraries. At this point, there is no reason why the VTK file +reader/writer should be any different. + +Thus, `VTKDataSetReader`, `VTKDataSetWriter`, and several supporting +classes are now compiled into the `vtk_io` library. + +As a side effect, code using VTK-m will need to link to `vtk_io` if they +are using any readers or writers. diff --git a/vtkm/io/CMakeLists.txt b/vtkm/io/CMakeLists.txt index 32d9d9ae5..a8d63aea4 100644 --- a/vtkm/io/CMakeLists.txt +++ b/vtkm/io/CMakeLists.txt @@ -35,7 +35,13 @@ set(template_sources set(sources DecodePNG.cxx EncodePNG.cxx + VTKDataSetReader.cxx VTKDataSetReaderBase.cxx + VTKPolyDataReader.cxx + VTKRectilinearGridReader.cxx + VTKStructuredGridReader.cxx + VTKStructuredPointsReader.cxx + VTKUnstructuredGridReader.cxx ) vtkm_declare_headers( diff --git a/vtkm/io/VTKDataSetReader.cxx b/vtkm/io/VTKDataSetReader.cxx new file mode 100644 index 000000000..a1fdbbdb9 --- /dev/null +++ b/vtkm/io/VTKDataSetReader.cxx @@ -0,0 +1,92 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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 + +#include +#include +#include +#include +#include + +#include + +namespace vtkm +{ +namespace io +{ + +VTKDataSetReader::VTKDataSetReader(const char* fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +VTKDataSetReader::VTKDataSetReader(const std::string& fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +VTKDataSetReader::~VTKDataSetReader() +{ +} + +void VTKDataSetReader::PrintSummary(std::ostream& out) const +{ + if (this->Reader) + { + this->Reader->PrintSummary(out); + } + else + { + VTKDataSetReaderBase::PrintSummary(out); + } +} + +void VTKDataSetReader::CloseFile() +{ + if (this->Reader) + { + this->Reader->CloseFile(); + } + else + { + VTKDataSetReaderBase::CloseFile(); + } +} + +void VTKDataSetReader::Read() +{ + switch (this->DataFile->Structure) + { + case vtkm::io::internal::DATASET_STRUCTURED_POINTS: + this->Reader.reset(new VTKStructuredPointsReader("")); + break; + case vtkm::io::internal::DATASET_STRUCTURED_GRID: + this->Reader.reset(new VTKStructuredGridReader("")); + break; + case vtkm::io::internal::DATASET_RECTILINEAR_GRID: + this->Reader.reset(new VTKRectilinearGridReader("")); + break; + case vtkm::io::internal::DATASET_POLYDATA: + this->Reader.reset(new VTKPolyDataReader("")); + break; + case vtkm::io::internal::DATASET_UNSTRUCTURED_GRID: + this->Reader.reset(new VTKUnstructuredGridReader("")); + break; + default: + throw vtkm::io::ErrorIO("Unsupported DataSet type."); + } + + this->TransferDataFile(*this->Reader.get()); + this->Reader->Read(); + this->DataSet = this->Reader->GetDataSet(); +} +} +} // namespace vtkm::io diff --git a/vtkm/io/VTKDataSetReader.h b/vtkm/io/VTKDataSetReader.h index 166c9466d..49e4b4537 100644 --- a/vtkm/io/VTKDataSetReader.h +++ b/vtkm/io/VTKDataSetReader.h @@ -11,86 +11,27 @@ #define vtk_m_io_VTKDataSetReader_h #include -#include -#include -#include -#include -#include - -#include namespace vtkm { namespace io { -VTKM_SILENCE_WEAK_VTABLE_WARNING_START - -class VTKDataSetReader : public VTKDataSetReaderBase +class VTKM_IO_EXPORT VTKDataSetReader : public VTKDataSetReaderBase { public: - explicit VTKDataSetReader(const char* fileName) - : VTKDataSetReaderBase(fileName) - { - } + VTKM_CONT VTKDataSetReader(const char* fileName); + VTKM_CONT VTKDataSetReader(const std::string& fileName); + VTKM_CONT ~VTKDataSetReader() override; - explicit VTKDataSetReader(const std::string& fileName) - : VTKDataSetReaderBase(fileName) - { - } + VTKDataSetReader(const VTKDataSetReader&) = delete; + void operator=(const VTKDataSetReader&) = delete; - virtual void PrintSummary(std::ostream& out) const - { - if (this->Reader) - { - this->Reader->PrintSummary(out); - } - else - { - VTKDataSetReaderBase::PrintSummary(out); - } - } + VTKM_CONT void PrintSummary(std::ostream& out) const override; private: - virtual void CloseFile() - { - if (this->Reader) - { - this->Reader->CloseFile(); - } - else - { - VTKDataSetReaderBase::CloseFile(); - } - } - - virtual void Read() - { - switch (this->DataFile->Structure) - { - case vtkm::io::internal::DATASET_STRUCTURED_POINTS: - this->Reader.reset(new VTKStructuredPointsReader("")); - break; - case vtkm::io::internal::DATASET_STRUCTURED_GRID: - this->Reader.reset(new VTKStructuredGridReader("")); - break; - case vtkm::io::internal::DATASET_RECTILINEAR_GRID: - this->Reader.reset(new VTKRectilinearGridReader("")); - break; - case vtkm::io::internal::DATASET_POLYDATA: - this->Reader.reset(new VTKPolyDataReader("")); - break; - case vtkm::io::internal::DATASET_UNSTRUCTURED_GRID: - this->Reader.reset(new VTKUnstructuredGridReader("")); - break; - default: - throw vtkm::io::ErrorIO("Unsupported DataSet type."); - } - - this->TransferDataFile(*this->Reader.get()); - this->Reader->Read(); - this->DataSet = this->Reader->GetDataSet(); - } + VTKM_CONT void CloseFile() override; + VTKM_CONT void Read() override; std::unique_ptr Reader; }; diff --git a/vtkm/io/VTKPolyDataReader.cxx b/vtkm/io/VTKPolyDataReader.cxx new file mode 100644 index 000000000..e134d6160 --- /dev/null +++ b/vtkm/io/VTKPolyDataReader.cxx @@ -0,0 +1,152 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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 + +namespace +{ + +template +inline vtkm::cont::ArrayHandle ConcatinateArrayHandles( + const std::vector>& arrays) +{ + vtkm::Id size = 0; + for (std::size_t i = 0; i < arrays.size(); ++i) + { + size += arrays[i].GetNumberOfValues(); + } + + vtkm::cont::ArrayHandle out; + out.Allocate(size); + + auto outp = vtkm::cont::ArrayPortalToIteratorBegin(out.WritePortal()); + for (std::size_t i = 0; i < arrays.size(); ++i) + { + std::copy(vtkm::cont::ArrayPortalToIteratorBegin(arrays[i].ReadPortal()), + vtkm::cont::ArrayPortalToIteratorEnd(arrays[i].ReadPortal()), + outp); + using DifferenceType = typename std::iterator_traits::difference_type; + std::advance(outp, static_cast(arrays[i].GetNumberOfValues())); + } + + return out; +} +} + +namespace vtkm +{ +namespace io +{ + +VTKPolyDataReader::VTKPolyDataReader(const char* fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +VTKPolyDataReader::VTKPolyDataReader(const std::string& fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +void VTKPolyDataReader::Read() +{ + if (this->DataFile->Structure != vtkm::io::internal::DATASET_POLYDATA) + { + throw vtkm::io::ErrorIO("Incorrect DataSet type"); + } + + //We need to be able to handle VisIt files which dump Field data + //at the top of a VTK file + std::string tag; + this->DataFile->Stream >> tag; + if (tag == "FIELD") + { + this->ReadGlobalFields(); + this->DataFile->Stream >> tag; + } + + // Read the points + internal::parseAssert(tag == "POINTS"); + this->ReadPoints(); + + vtkm::Id numPoints = this->DataSet.GetNumberOfPoints(); + + // Read the cellset + std::vector> connectivityArrays; + std::vector> numIndicesArrays; + std::vector shapesBuffer; + while (!this->DataFile->Stream.eof()) + { + vtkm::UInt8 shape = vtkm::CELL_SHAPE_EMPTY; + this->DataFile->Stream >> tag; + if (tag == "VERTICES") + { + shape = vtkm::io::internal::CELL_SHAPE_POLY_VERTEX; + } + else if (tag == "LINES") + { + shape = vtkm::io::internal::CELL_SHAPE_POLY_LINE; + } + else if (tag == "POLYGONS") + { + shape = vtkm::CELL_SHAPE_POLYGON; + } + else if (tag == "TRIANGLE_STRIPS") + { + shape = vtkm::io::internal::CELL_SHAPE_TRIANGLE_STRIP; + } + else + { + this->DataFile->Stream.seekg(-static_cast(tag.length()), std::ios_base::cur); + break; + } + + vtkm::cont::ArrayHandle cellConnectivity; + vtkm::cont::ArrayHandle cellNumIndices; + this->ReadCells(cellConnectivity, cellNumIndices); + + connectivityArrays.push_back(cellConnectivity); + numIndicesArrays.push_back(cellNumIndices); + shapesBuffer.insert( + shapesBuffer.end(), static_cast(cellNumIndices.GetNumberOfValues()), shape); + } + + vtkm::cont::ArrayHandle connectivity = ConcatinateArrayHandles(connectivityArrays); + vtkm::cont::ArrayHandle numIndices = ConcatinateArrayHandles(numIndicesArrays); + vtkm::cont::ArrayHandle shapes; + shapes.Allocate(static_cast(shapesBuffer.size())); + std::copy(shapesBuffer.begin(), + shapesBuffer.end(), + vtkm::cont::ArrayPortalToIteratorBegin(shapes.WritePortal())); + + vtkm::cont::ArrayHandle permutation; + vtkm::io::internal::FixupCellSet(connectivity, numIndices, shapes, permutation); + this->SetCellsPermutation(permutation); + + if (vtkm::io::internal::IsSingleShape(shapes)) + { + vtkm::cont::CellSetSingleType<> cellSet; + cellSet.Fill( + numPoints, shapes.ReadPortal().Get(0), numIndices.ReadPortal().Get(0), connectivity); + this->DataSet.SetCellSet(cellSet); + } + else + { + auto offsets = vtkm::cont::ConvertNumIndicesToOffsets(numIndices); + vtkm::cont::CellSetExplicit<> cellSet; + cellSet.Fill(numPoints, shapes, connectivity, offsets); + this->DataSet.SetCellSet(cellSet); + } + + // Read points and cell attributes + this->ReadAttributes(); +} +} +} // namespace vtkm::io diff --git a/vtkm/io/VTKPolyDataReader.h b/vtkm/io/VTKPolyDataReader.h index dcc0623f7..449c1747f 100644 --- a/vtkm/io/VTKPolyDataReader.h +++ b/vtkm/io/VTKPolyDataReader.h @@ -22,147 +22,15 @@ namespace vtkm namespace io { -namespace internal -{ - -template -inline vtkm::cont::ArrayHandle ConcatinateArrayHandles( - const std::vector>& arrays) -{ - vtkm::Id size = 0; - for (std::size_t i = 0; i < arrays.size(); ++i) - { - size += arrays[i].GetNumberOfValues(); - } - - vtkm::cont::ArrayHandle out; - out.Allocate(size); - - auto outp = vtkm::cont::ArrayPortalToIteratorBegin(out.WritePortal()); - for (std::size_t i = 0; i < arrays.size(); ++i) - { - std::copy(vtkm::cont::ArrayPortalToIteratorBegin(arrays[i].ReadPortal()), - vtkm::cont::ArrayPortalToIteratorEnd(arrays[i].ReadPortal()), - outp); - using DifferenceType = typename std::iterator_traits::difference_type; - std::advance(outp, static_cast(arrays[i].GetNumberOfValues())); - } - - return out; -} - -} // namespace internal - -VTKM_SILENCE_WEAK_VTABLE_WARNING_START - -class VTKPolyDataReader : public VTKDataSetReaderBase +class VTKM_IO_EXPORT VTKPolyDataReader : public VTKDataSetReaderBase { public: - explicit VTKPolyDataReader(const char* fileName) - : VTKDataSetReaderBase(fileName) - { - } + explicit VTKM_CONT VTKPolyDataReader(const char* fileName); + explicit VTKM_CONT VTKPolyDataReader(const std::string& fileName); private: - virtual void Read() - { - if (this->DataFile->Structure != vtkm::io::internal::DATASET_POLYDATA) - { - throw vtkm::io::ErrorIO("Incorrect DataSet type"); - } - - //We need to be able to handle VisIt files which dump Field data - //at the top of a VTK file - std::string tag; - this->DataFile->Stream >> tag; - if (tag == "FIELD") - { - this->ReadGlobalFields(); - this->DataFile->Stream >> tag; - } - - // Read the points - internal::parseAssert(tag == "POINTS"); - this->ReadPoints(); - - vtkm::Id numPoints = this->DataSet.GetNumberOfPoints(); - - // Read the cellset - std::vector> connectivityArrays; - std::vector> numIndicesArrays; - std::vector shapesBuffer; - while (!this->DataFile->Stream.eof()) - { - vtkm::UInt8 shape = vtkm::CELL_SHAPE_EMPTY; - this->DataFile->Stream >> tag; - if (tag == "VERTICES") - { - shape = vtkm::io::internal::CELL_SHAPE_POLY_VERTEX; - } - else if (tag == "LINES") - { - shape = vtkm::io::internal::CELL_SHAPE_POLY_LINE; - } - else if (tag == "POLYGONS") - { - shape = vtkm::CELL_SHAPE_POLYGON; - } - else if (tag == "TRIANGLE_STRIPS") - { - shape = vtkm::io::internal::CELL_SHAPE_TRIANGLE_STRIP; - } - else - { - this->DataFile->Stream.seekg(-static_cast(tag.length()), - std::ios_base::cur); - break; - } - - vtkm::cont::ArrayHandle cellConnectivity; - vtkm::cont::ArrayHandle cellNumIndices; - this->ReadCells(cellConnectivity, cellNumIndices); - - connectivityArrays.push_back(cellConnectivity); - numIndicesArrays.push_back(cellNumIndices); - shapesBuffer.insert( - shapesBuffer.end(), static_cast(cellNumIndices.GetNumberOfValues()), shape); - } - - vtkm::cont::ArrayHandle connectivity = - internal::ConcatinateArrayHandles(connectivityArrays); - vtkm::cont::ArrayHandle numIndices = - internal::ConcatinateArrayHandles(numIndicesArrays); - vtkm::cont::ArrayHandle shapes; - shapes.Allocate(static_cast(shapesBuffer.size())); - std::copy(shapesBuffer.begin(), - shapesBuffer.end(), - vtkm::cont::ArrayPortalToIteratorBegin(shapes.WritePortal())); - - vtkm::cont::ArrayHandle permutation; - vtkm::io::internal::FixupCellSet(connectivity, numIndices, shapes, permutation); - this->SetCellsPermutation(permutation); - - if (vtkm::io::internal::IsSingleShape(shapes)) - { - vtkm::cont::CellSetSingleType<> cellSet; - cellSet.Fill( - numPoints, shapes.ReadPortal().Get(0), numIndices.ReadPortal().Get(0), connectivity); - this->DataSet.SetCellSet(cellSet); - } - else - { - auto offsets = vtkm::cont::ConvertNumIndicesToOffsets(numIndices); - vtkm::cont::CellSetExplicit<> cellSet; - cellSet.Fill(numPoints, shapes, connectivity, offsets); - this->DataSet.SetCellSet(cellSet); - } - - // Read points and cell attributes - this->ReadAttributes(); - } + void Read() override; }; - -VTKM_SILENCE_WEAK_VTABLE_WARNING_END } } // namespace vtkm::io diff --git a/vtkm/io/VTKRectilinearGridReader.cxx b/vtkm/io/VTKRectilinearGridReader.cxx new file mode 100644 index 000000000..9dcf3a5ca --- /dev/null +++ b/vtkm/io/VTKRectilinearGridReader.cxx @@ -0,0 +1,95 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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 + +namespace vtkm +{ +namespace io +{ + +VTKRectilinearGridReader::VTKRectilinearGridReader(const char* fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +VTKRectilinearGridReader::VTKRectilinearGridReader(const std::string& fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +void VTKRectilinearGridReader::Read() +{ + if (this->DataFile->Structure != vtkm::io::internal::DATASET_RECTILINEAR_GRID) + throw vtkm::io::ErrorIO("Incorrect DataSet type"); + + //We need to be able to handle VisIt files which dump Field data + //at the top of a VTK file + std::string tag; + this->DataFile->Stream >> tag; + if (tag == "FIELD") + { + this->ReadGlobalFields(); + this->DataFile->Stream >> tag; + } + + // Read structured grid specific meta-data + internal::parseAssert(tag == "DIMENSIONS"); + vtkm::Id3 dim; + this->DataFile->Stream >> dim[0] >> dim[1] >> dim[2] >> std::ws; + + //Read the points. + std::string dataType; + std::size_t numPoints[3]; + vtkm::cont::VariantArrayHandle X, Y, Z; + + // Always read coordinates as vtkm::FloatDefault + std::string readDataType = vtkm::io::internal::DataTypeName::Name(); + + this->DataFile->Stream >> tag >> numPoints[0] >> dataType >> std::ws; + if (tag != "X_COORDINATES") + throw vtkm::io::ErrorIO("X_COORDINATES tag not found"); + X = this->DoReadArrayVariant(vtkm::cont::Field::Association::ANY, readDataType, numPoints[0], 1); + + this->DataFile->Stream >> tag >> numPoints[1] >> dataType >> std::ws; + if (tag != "Y_COORDINATES") + throw vtkm::io::ErrorIO("Y_COORDINATES tag not found"); + Y = this->DoReadArrayVariant(vtkm::cont::Field::Association::ANY, readDataType, numPoints[1], 1); + + this->DataFile->Stream >> tag >> numPoints[2] >> dataType >> std::ws; + if (tag != "Z_COORDINATES") + throw vtkm::io::ErrorIO("Z_COORDINATES tag not found"); + Z = this->DoReadArrayVariant(vtkm::cont::Field::Association::ANY, readDataType, numPoints[2], 1); + + if (dim != vtkm::Id3(static_cast(numPoints[0]), + static_cast(numPoints[1]), + static_cast(numPoints[2]))) + throw vtkm::io::ErrorIO("DIMENSIONS not equal to number of points"); + + vtkm::cont::ArrayHandleCartesianProduct, + vtkm::cont::ArrayHandle, + vtkm::cont::ArrayHandle> + coords; + + vtkm::cont::ArrayHandle Xc, Yc, Zc; + X.CopyTo(Xc); + Y.CopyTo(Yc); + Z.CopyTo(Zc); + coords = vtkm::cont::make_ArrayHandleCartesianProduct(Xc, Yc, Zc); + vtkm::cont::CoordinateSystem coordSys("coordinates", coords); + this->DataSet.AddCoordinateSystem(coordSys); + + this->DataSet.SetCellSet(internal::CreateCellSetStructured(dim)); + + // Read points and cell attributes + this->ReadAttributes(); +} +} +} // namespace vtkm::io diff --git a/vtkm/io/VTKRectilinearGridReader.h b/vtkm/io/VTKRectilinearGridReader.h index 92a5c6e05..37c681c28 100644 --- a/vtkm/io/VTKRectilinearGridReader.h +++ b/vtkm/io/VTKRectilinearGridReader.h @@ -17,89 +17,15 @@ namespace vtkm namespace io { -VTKM_SILENCE_WEAK_VTABLE_WARNING_START - -class VTKRectilinearGridReader : public VTKDataSetReaderBase +class VTKM_IO_EXPORT VTKRectilinearGridReader : public VTKDataSetReaderBase { public: - explicit VTKRectilinearGridReader(const char* fileName) - : VTKDataSetReaderBase(fileName) - { - } + explicit VTKM_CONT VTKRectilinearGridReader(const char* fileName); + explicit VTKM_CONT VTKRectilinearGridReader(const std::string& fileName); private: - virtual void Read() - { - if (this->DataFile->Structure != vtkm::io::internal::DATASET_RECTILINEAR_GRID) - throw vtkm::io::ErrorIO("Incorrect DataSet type"); - - //We need to be able to handle VisIt files which dump Field data - //at the top of a VTK file - std::string tag; - this->DataFile->Stream >> tag; - if (tag == "FIELD") - { - this->ReadGlobalFields(); - this->DataFile->Stream >> tag; - } - - // Read structured grid specific meta-data - internal::parseAssert(tag == "DIMENSIONS"); - vtkm::Id3 dim; - this->DataFile->Stream >> dim[0] >> dim[1] >> dim[2] >> std::ws; - - //Read the points. - std::string dataType; - std::size_t numPoints[3]; - vtkm::cont::VariantArrayHandle X, Y, Z; - - // Always read coordinates as vtkm::FloatDefault - std::string readDataType = vtkm::io::internal::DataTypeName::Name(); - - this->DataFile->Stream >> tag >> numPoints[0] >> dataType >> std::ws; - if (tag != "X_COORDINATES") - throw vtkm::io::ErrorIO("X_COORDINATES tag not found"); - X = - this->DoReadArrayVariant(vtkm::cont::Field::Association::ANY, readDataType, numPoints[0], 1); - - this->DataFile->Stream >> tag >> numPoints[1] >> dataType >> std::ws; - if (tag != "Y_COORDINATES") - throw vtkm::io::ErrorIO("Y_COORDINATES tag not found"); - Y = - this->DoReadArrayVariant(vtkm::cont::Field::Association::ANY, readDataType, numPoints[1], 1); - - this->DataFile->Stream >> tag >> numPoints[2] >> dataType >> std::ws; - if (tag != "Z_COORDINATES") - throw vtkm::io::ErrorIO("Z_COORDINATES tag not found"); - Z = - this->DoReadArrayVariant(vtkm::cont::Field::Association::ANY, readDataType, numPoints[2], 1); - - if (dim != vtkm::Id3(static_cast(numPoints[0]), - static_cast(numPoints[1]), - static_cast(numPoints[2]))) - throw vtkm::io::ErrorIO("DIMENSIONS not equal to number of points"); - - vtkm::cont::ArrayHandleCartesianProduct, - vtkm::cont::ArrayHandle, - vtkm::cont::ArrayHandle> - coords; - - vtkm::cont::ArrayHandle Xc, Yc, Zc; - X.CopyTo(Xc); - Y.CopyTo(Yc); - Z.CopyTo(Zc); - coords = vtkm::cont::make_ArrayHandleCartesianProduct(Xc, Yc, Zc); - vtkm::cont::CoordinateSystem coordSys("coordinates", coords); - this->DataSet.AddCoordinateSystem(coordSys); - - this->DataSet.SetCellSet(internal::CreateCellSetStructured(dim)); - - // Read points and cell attributes - this->ReadAttributes(); - } + VTKM_CONT void Read() override; }; - -VTKM_SILENCE_WEAK_VTABLE_WARNING_END } } // namespace vtkm::io diff --git a/vtkm/io/VTKStructuredGridReader.cxx b/vtkm/io/VTKStructuredGridReader.cxx new file mode 100644 index 000000000..76b7d2101 --- /dev/null +++ b/vtkm/io/VTKStructuredGridReader.cxx @@ -0,0 +1,62 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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 + +namespace vtkm +{ +namespace io +{ + +VTKStructuredGridReader::VTKStructuredGridReader(const char* fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +VTKStructuredGridReader::VTKStructuredGridReader(const std::string& fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +void VTKStructuredGridReader::Read() +{ + if (this->DataFile->Structure != vtkm::io::internal::DATASET_STRUCTURED_GRID) + { + throw vtkm::io::ErrorIO("Incorrect DataSet type"); + } + + std::string tag; + + //We need to be able to handle VisIt files which dump Field data + //at the top of a VTK file + this->DataFile->Stream >> tag; + if (tag == "FIELD") + { + this->ReadGlobalFields(); + this->DataFile->Stream >> tag; + } + + // Read structured grid specific meta-data + internal::parseAssert(tag == "DIMENSIONS"); + vtkm::Id3 dim; + this->DataFile->Stream >> dim[0] >> dim[1] >> dim[2] >> std::ws; + + this->DataSet.SetCellSet(internal::CreateCellSetStructured(dim)); + + // Read the points + this->DataFile->Stream >> tag; + internal::parseAssert(tag == "POINTS"); + this->ReadPoints(); + + // Read points and cell attributes + this->ReadAttributes(); +} +} +} // namespace vtkm::io diff --git a/vtkm/io/VTKStructuredGridReader.h b/vtkm/io/VTKStructuredGridReader.h index b37e98301..745cef2b7 100644 --- a/vtkm/io/VTKStructuredGridReader.h +++ b/vtkm/io/VTKStructuredGridReader.h @@ -17,53 +17,15 @@ namespace vtkm namespace io { -VTKM_SILENCE_WEAK_VTABLE_WARNING_START - -class VTKStructuredGridReader : public VTKDataSetReaderBase +class VTKM_IO_EXPORT VTKStructuredGridReader : public VTKDataSetReaderBase { public: - explicit VTKStructuredGridReader(const char* fileName) - : VTKDataSetReaderBase(fileName) - { - } + explicit VTKM_CONT VTKStructuredGridReader(const char* fileName); + explicit VTKM_CONT VTKStructuredGridReader(const std::string& fileName); private: - virtual void Read() - { - if (this->DataFile->Structure != vtkm::io::internal::DATASET_STRUCTURED_GRID) - { - throw vtkm::io::ErrorIO("Incorrect DataSet type"); - } - - std::string tag; - - //We need to be able to handle VisIt files which dump Field data - //at the top of a VTK file - this->DataFile->Stream >> tag; - if (tag == "FIELD") - { - this->ReadGlobalFields(); - this->DataFile->Stream >> tag; - } - - // Read structured grid specific meta-data - internal::parseAssert(tag == "DIMENSIONS"); - vtkm::Id3 dim; - this->DataFile->Stream >> dim[0] >> dim[1] >> dim[2] >> std::ws; - - this->DataSet.SetCellSet(internal::CreateCellSetStructured(dim)); - - // Read the points - this->DataFile->Stream >> tag; - internal::parseAssert(tag == "POINTS"); - this->ReadPoints(); - - // Read points and cell attributes - this->ReadAttributes(); - } + VTKM_CONT void Read() override; }; - -VTKM_SILENCE_WEAK_VTABLE_WARNING_END } } // namespace vtkm::io diff --git a/vtkm/io/VTKStructuredPointsReader.cxx b/vtkm/io/VTKStructuredPointsReader.cxx new file mode 100644 index 000000000..1293da374 --- /dev/null +++ b/vtkm/io/VTKStructuredPointsReader.cxx @@ -0,0 +1,79 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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 + +namespace vtkm +{ +namespace io +{ + +VTKStructuredPointsReader::VTKStructuredPointsReader(const char* fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +VTKStructuredPointsReader::VTKStructuredPointsReader(const std::string& fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +void VTKStructuredPointsReader::Read() +{ + if (this->DataFile->Structure != vtkm::io::internal::DATASET_STRUCTURED_POINTS) + { + throw vtkm::io::ErrorIO("Incorrect DataSet type"); + } + + std::string tag; + + // Read structured points specific meta-data + vtkm::Id3 dim; + vtkm::Vec3f_32 origin, spacing; + + //Two ways the file can describe the dimensions. The proper way is by + //using the DIMENSIONS keyword, but VisIt written VTK files spicify data + //bounds instead, as a FIELD + std::vector visitBounds; + this->DataFile->Stream >> tag; + if (tag == "FIELD") + { + this->ReadGlobalFields(&visitBounds); + this->DataFile->Stream >> tag; + } + if (visitBounds.empty()) + { + internal::parseAssert(tag == "DIMENSIONS"); + this->DataFile->Stream >> dim[0] >> dim[1] >> dim[2] >> std::ws; + this->DataFile->Stream >> tag; + } + + internal::parseAssert(tag == "SPACING"); + this->DataFile->Stream >> spacing[0] >> spacing[1] >> spacing[2] >> std::ws; + if (!visitBounds.empty()) + { + //now with spacing and physical bounds we can back compute the dimensions + dim[0] = static_cast((visitBounds[1] - visitBounds[0]) / spacing[0]); + dim[1] = static_cast((visitBounds[3] - visitBounds[2]) / spacing[1]); + dim[2] = static_cast((visitBounds[5] - visitBounds[4]) / spacing[2]); + } + + this->DataFile->Stream >> tag >> origin[0] >> origin[1] >> origin[2] >> std::ws; + internal::parseAssert(tag == "ORIGIN"); + + this->DataSet.SetCellSet(internal::CreateCellSetStructured(dim)); + this->DataSet.AddCoordinateSystem( + vtkm::cont::CoordinateSystem("coordinates", dim, origin, spacing)); + + // Read points and cell attributes + this->ReadAttributes(); +} +} +} // namespace vtkm::io diff --git a/vtkm/io/VTKStructuredPointsReader.h b/vtkm/io/VTKStructuredPointsReader.h index 17d786380..2e998f96f 100644 --- a/vtkm/io/VTKStructuredPointsReader.h +++ b/vtkm/io/VTKStructuredPointsReader.h @@ -17,70 +17,15 @@ namespace vtkm namespace io { -VTKM_SILENCE_WEAK_VTABLE_WARNING_START - -class VTKStructuredPointsReader : public VTKDataSetReaderBase +class VTKM_IO_EXPORT VTKStructuredPointsReader : public VTKDataSetReaderBase { public: - explicit VTKStructuredPointsReader(const char* fileName) - : VTKDataSetReaderBase(fileName) - { - } + explicit VTKM_CONT VTKStructuredPointsReader(const char* fileName); + explicit VTKM_CONT VTKStructuredPointsReader(const std::string& fileName); private: - virtual void Read() - { - if (this->DataFile->Structure != vtkm::io::internal::DATASET_STRUCTURED_POINTS) - { - throw vtkm::io::ErrorIO("Incorrect DataSet type"); - } - - std::string tag; - - // Read structured points specific meta-data - vtkm::Id3 dim; - vtkm::Vec3f_32 origin, spacing; - - //Two ways the file can describe the dimensions. The proper way is by - //using the DIMENSIONS keyword, but VisIt written VTK files spicify data - //bounds instead, as a FIELD - std::vector visitBounds; - this->DataFile->Stream >> tag; - if (tag == "FIELD") - { - this->ReadGlobalFields(&visitBounds); - this->DataFile->Stream >> tag; - } - if (visitBounds.empty()) - { - internal::parseAssert(tag == "DIMENSIONS"); - this->DataFile->Stream >> dim[0] >> dim[1] >> dim[2] >> std::ws; - this->DataFile->Stream >> tag; - } - - internal::parseAssert(tag == "SPACING"); - this->DataFile->Stream >> spacing[0] >> spacing[1] >> spacing[2] >> std::ws; - if (!visitBounds.empty()) - { - //now with spacing and physical bounds we can back compute the dimensions - dim[0] = static_cast((visitBounds[1] - visitBounds[0]) / spacing[0]); - dim[1] = static_cast((visitBounds[3] - visitBounds[2]) / spacing[1]); - dim[2] = static_cast((visitBounds[5] - visitBounds[4]) / spacing[2]); - } - - this->DataFile->Stream >> tag >> origin[0] >> origin[1] >> origin[2] >> std::ws; - internal::parseAssert(tag == "ORIGIN"); - - this->DataSet.SetCellSet(internal::CreateCellSetStructured(dim)); - this->DataSet.AddCoordinateSystem( - vtkm::cont::CoordinateSystem("coordinates", dim, origin, spacing)); - - // Read points and cell attributes - this->ReadAttributes(); - } + VTKM_CONT void Read() override; }; - -VTKM_SILENCE_WEAK_VTABLE_WARNING_END } } // namespace vtkm::io diff --git a/vtkm/io/VTKUnstructuredGridReader.cxx b/vtkm/io/VTKUnstructuredGridReader.cxx new file mode 100644 index 000000000..0fb69cacb --- /dev/null +++ b/vtkm/io/VTKUnstructuredGridReader.cxx @@ -0,0 +1,88 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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 + +#include + +namespace vtkm +{ +namespace io +{ + +VTKUnstructuredGridReader::VTKUnstructuredGridReader(const char* fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +VTKUnstructuredGridReader::VTKUnstructuredGridReader(const std::string& fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +void VTKUnstructuredGridReader::Read() +{ + if (this->DataFile->Structure != vtkm::io::internal::DATASET_UNSTRUCTURED_GRID) + { + throw vtkm::io::ErrorIO("Incorrect DataSet type"); + } + + //We need to be able to handle VisIt files which dump Field data + //at the top of a VTK file + std::string tag; + this->DataFile->Stream >> tag; + if (tag == "FIELD") + { + this->ReadGlobalFields(); + this->DataFile->Stream >> tag; + } + + // Read the points + internal::parseAssert(tag == "POINTS"); + this->ReadPoints(); + + vtkm::Id numPoints = this->DataSet.GetNumberOfPoints(); + + // Read the cellset + vtkm::cont::ArrayHandle connectivity; + vtkm::cont::ArrayHandle numIndices; + vtkm::cont::ArrayHandle shapes; + + this->DataFile->Stream >> tag; + internal::parseAssert(tag == "CELLS"); + + this->ReadCells(connectivity, numIndices); + this->ReadShapes(shapes); + + vtkm::cont::ArrayHandle permutation; + vtkm::io::internal::FixupCellSet(connectivity, numIndices, shapes, permutation); + this->SetCellsPermutation(permutation); + + //DRP + if (false) //vtkm::io::internal::IsSingleShape(shapes)) + { + vtkm::cont::CellSetSingleType<> cellSet; + cellSet.Fill( + numPoints, shapes.ReadPortal().Get(0), numIndices.ReadPortal().Get(0), connectivity); + this->DataSet.SetCellSet(cellSet); + } + else + { + auto offsets = vtkm::cont::ConvertNumIndicesToOffsets(numIndices); + vtkm::cont::CellSetExplicit<> cellSet; + cellSet.Fill(numPoints, shapes, connectivity, offsets); + this->DataSet.SetCellSet(cellSet); + } + + // Read points and cell attributes + this->ReadAttributes(); +} +} +} diff --git a/vtkm/io/VTKUnstructuredGridReader.h b/vtkm/io/VTKUnstructuredGridReader.h index bee4e06f8..4d60e1d39 100644 --- a/vtkm/io/VTKUnstructuredGridReader.h +++ b/vtkm/io/VTKUnstructuredGridReader.h @@ -17,77 +17,15 @@ namespace vtkm namespace io { -VTKM_SILENCE_WEAK_VTABLE_WARNING_START - -class VTKUnstructuredGridReader : public VTKDataSetReaderBase +class VTKM_IO_EXPORT VTKUnstructuredGridReader : public VTKDataSetReaderBase { public: - explicit VTKUnstructuredGridReader(const char* fileName) - : VTKDataSetReaderBase(fileName) - { - } + explicit VTKM_CONT VTKUnstructuredGridReader(const char* fileName); + explicit VTKM_CONT VTKUnstructuredGridReader(const std::string& fileName); private: - virtual void Read() - { - if (this->DataFile->Structure != vtkm::io::internal::DATASET_UNSTRUCTURED_GRID) - { - throw vtkm::io::ErrorIO("Incorrect DataSet type"); - } - - //We need to be able to handle VisIt files which dump Field data - //at the top of a VTK file - std::string tag; - this->DataFile->Stream >> tag; - if (tag == "FIELD") - { - this->ReadGlobalFields(); - this->DataFile->Stream >> tag; - } - - // Read the points - internal::parseAssert(tag == "POINTS"); - this->ReadPoints(); - - vtkm::Id numPoints = this->DataSet.GetNumberOfPoints(); - - // Read the cellset - vtkm::cont::ArrayHandle connectivity; - vtkm::cont::ArrayHandle numIndices; - vtkm::cont::ArrayHandle shapes; - - this->DataFile->Stream >> tag; - internal::parseAssert(tag == "CELLS"); - - this->ReadCells(connectivity, numIndices); - this->ReadShapes(shapes); - - vtkm::cont::ArrayHandle permutation; - vtkm::io::internal::FixupCellSet(connectivity, numIndices, shapes, permutation); - this->SetCellsPermutation(permutation); - - //DRP - if (false) //vtkm::io::internal::IsSingleShape(shapes)) - { - vtkm::cont::CellSetSingleType<> cellSet; - cellSet.Fill( - numPoints, shapes.ReadPortal().Get(0), numIndices.ReadPortal().Get(0), connectivity); - this->DataSet.SetCellSet(cellSet); - } - else - { - auto offsets = vtkm::cont::ConvertNumIndicesToOffsets(numIndices); - vtkm::cont::CellSetExplicit<> cellSet; - cellSet.Fill(numPoints, shapes, connectivity, offsets); - this->DataSet.SetCellSet(cellSet); - } - - // Read points and cell attributes - this->ReadAttributes(); - } + VTKM_CONT void Read() override; }; - -VTKM_SILENCE_WEAK_VTABLE_WARNING_END } } // namespace vtkm::io From 8657a9b3c88c5223ad02ebc05799f3a648dcf457 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Tue, 19 May 2020 13:29:04 -0600 Subject: [PATCH 3/5] Update VTKDataSetWriter to compile into vtkm_io --- examples/clipping/CMakeLists.txt | 2 +- examples/hello_worklet/CMakeLists.txt | 2 +- examples/tetrahedra/CMakeLists.txt | 4 +- vtkm/io/CMakeLists.txt | 3 +- vtkm/io/VTKDataSetReaderBase.h | 3 - vtkm/io/VTKDataSetWriter.cxx | 407 ++++++++++++++++++++++++++ vtkm/io/VTKDataSetWriter.h | 388 +----------------------- vtkm/io/internal/VTKDataSetTypes.h | 4 + 8 files changed, 422 insertions(+), 391 deletions(-) create mode 100644 vtkm/io/VTKDataSetWriter.cxx diff --git a/examples/clipping/CMakeLists.txt b/examples/clipping/CMakeLists.txt index 6f26353ce..46bc5bf18 100644 --- a/examples/clipping/CMakeLists.txt +++ b/examples/clipping/CMakeLists.txt @@ -14,7 +14,7 @@ project(Clipping CXX) find_package(VTKm REQUIRED QUIET) add_executable(Clipping Clipping.cxx) -target_link_libraries(Clipping PRIVATE vtkm_filter) +target_link_libraries(Clipping PRIVATE vtkm_filter vtkm_io) vtkm_add_target_information(Clipping DROP_UNUSED_SYMBOLS MODIFY_CUDA_FLAGS diff --git a/examples/hello_worklet/CMakeLists.txt b/examples/hello_worklet/CMakeLists.txt index 806f69b57..eff9dfcae 100644 --- a/examples/hello_worklet/CMakeLists.txt +++ b/examples/hello_worklet/CMakeLists.txt @@ -14,7 +14,7 @@ project(HelloWorklet CXX) find_package(VTKm REQUIRED QUIET) add_executable(HelloWorklet HelloWorklet.cxx) -target_link_libraries(HelloWorklet PRIVATE vtkm_filter) +target_link_libraries(HelloWorklet PRIVATE vtkm_filter vtkm_io) vtkm_add_target_information(HelloWorklet DROP_UNUSED_SYMBOLS MODIFY_CUDA_FLAGS diff --git a/examples/tetrahedra/CMakeLists.txt b/examples/tetrahedra/CMakeLists.txt index c894daee5..7bd8a4e67 100644 --- a/examples/tetrahedra/CMakeLists.txt +++ b/examples/tetrahedra/CMakeLists.txt @@ -14,10 +14,10 @@ project(Tetrahedra CXX) find_package(VTKm REQUIRED QUIET) add_executable(Tetrahedralize Tetrahedralize.cxx) -target_link_libraries(Tetrahedralize PRIVATE vtkm_filter) +target_link_libraries(Tetrahedralize PRIVATE vtkm_filter vtkm_io) add_executable(Triangulate Triangulate.cxx) -target_link_libraries(Triangulate PRIVATE vtkm_filter) +target_link_libraries(Triangulate PRIVATE vtkm_filter vtkm_io) vtkm_add_target_information(Tetrahedralize Triangulate DROP_UNUSED_SYMBOLS diff --git a/vtkm/io/CMakeLists.txt b/vtkm/io/CMakeLists.txt index a8d63aea4..7b3e48d84 100644 --- a/vtkm/io/CMakeLists.txt +++ b/vtkm/io/CMakeLists.txt @@ -18,12 +18,12 @@ set(headers PixelTypes.h VTKDataSetReader.h VTKDataSetReaderBase.h + VTKDataSetWriter.h VTKPolyDataReader.h VTKRectilinearGridReader.h VTKStructuredGridReader.h VTKStructuredPointsReader.h VTKUnstructuredGridReader.h - VTKDataSetWriter.h ) set(template_sources @@ -37,6 +37,7 @@ set(sources EncodePNG.cxx VTKDataSetReader.cxx VTKDataSetReaderBase.cxx + VTKDataSetWriter.cxx VTKPolyDataReader.cxx VTKRectilinearGridReader.cxx VTKStructuredGridReader.cxx diff --git a/vtkm/io/VTKDataSetReaderBase.h b/vtkm/io/VTKDataSetReaderBase.h index 62a935315..245d46eb0 100644 --- a/vtkm/io/VTKDataSetReaderBase.h +++ b/vtkm/io/VTKDataSetReaderBase.h @@ -275,7 +275,4 @@ protected: } } // vtkm::io -VTKM_BASIC_TYPE_VECTOR(vtkm::io::internal::ColorChannel8) -VTKM_BASIC_TYPE_VECTOR(vtkm::io::internal::DummyBitType) - #endif // vtk_m_io_VTKDataSetReaderBase_h diff --git a/vtkm/io/VTKDataSetWriter.cxx b/vtkm/io/VTKDataSetWriter.cxx new file mode 100644 index 000000000..6f3eac1a3 --- /dev/null +++ b/vtkm/io/VTKDataSetWriter.cxx @@ -0,0 +1,407 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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 + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + +struct OutputPointsFunctor +{ +private: + std::ostream& out; + + template + VTKM_CONT void Output(const PortalType& portal) const + { + for (vtkm::Id index = 0; index < portal.GetNumberOfValues(); index++) + { + const int VTKDims = 3; // VTK files always require 3 dims for points + + using ValueType = typename PortalType::ValueType; + using VecType = typename vtkm::VecTraits; + + const ValueType& value = portal.Get(index); + + vtkm::IdComponent numComponents = VecType::GetNumberOfComponents(value); + for (vtkm::IdComponent c = 0; c < numComponents && c < VTKDims; c++) + { + out << (c == 0 ? "" : " ") << VecType::GetComponent(value, c); + } + for (vtkm::IdComponent c = numComponents; c < VTKDims; c++) + { + out << " 0"; + } + out << '\n'; + } + } + +public: + VTKM_CONT + OutputPointsFunctor(std::ostream& o) + : out(o) + { + } + + template + VTKM_CONT void operator()(const vtkm::cont::ArrayHandle& array) const + { + this->Output(array.ReadPortal()); + } +}; + +struct OutputFieldFunctor +{ +private: + std::ostream& out; + + template + VTKM_CONT void Output(const PortalType& portal) const + { + for (vtkm::Id index = 0; index < portal.GetNumberOfValues(); index++) + { + using ValueType = typename PortalType::ValueType; + using VecType = typename vtkm::VecTraits; + + const ValueType& value = portal.Get(index); + + vtkm::IdComponent numComponents = VecType::GetNumberOfComponents(value); + for (vtkm::IdComponent c = 0; c < numComponents; c++) + { + out << (c == 0 ? "" : " ") << VecType::GetComponent(value, c); + } + out << '\n'; + } + } + +public: + VTKM_CONT + OutputFieldFunctor(std::ostream& o) + : out(o) + { + } + + template + VTKM_CONT void operator()(const vtkm::cont::ArrayHandle& array) const + { + this->Output(array.ReadPortal()); + } +}; + +class GetDataTypeName +{ +public: + GetDataTypeName(std::string& name) + : Name(&name) + { + } + + template + void operator()(const ArrayHandleType&) const + { + using DataType = typename vtkm::VecTraits::ComponentType; + *this->Name = vtkm::io::internal::DataTypeName::Name(); + } + +private: + std::string* Name; +}; + +void WritePoints(std::ostream& out, const vtkm::cont::DataSet& dataSet) +{ + ///\todo: support other coordinate systems + int cindex = 0; + auto cdata = dataSet.GetCoordinateSystem(cindex).GetData(); + + vtkm::Id npoints = cdata.GetNumberOfValues(); + out << "POINTS " << npoints << " " << vtkm::io::internal::DataTypeName::Name() + << " " << '\n'; + + OutputPointsFunctor{ out }(cdata); +} + +template +void WriteExplicitCells(std::ostream& out, const CellSetType& cellSet) +{ + vtkm::Id nCells = cellSet.GetNumberOfCells(); + + vtkm::Id conn_length = 0; + for (vtkm::Id i = 0; i < nCells; ++i) + { + conn_length += 1 + cellSet.GetNumberOfPointsInCell(i); + } + + out << "CELLS " << nCells << " " << conn_length << '\n'; + + for (vtkm::Id i = 0; i < nCells; ++i) + { + vtkm::cont::ArrayHandle ids; + vtkm::Id nids = cellSet.GetNumberOfPointsInCell(i); + cellSet.GetIndices(i, ids); + out << nids; + auto IdPortal = ids.ReadPortal(); + for (int j = 0; j < nids; ++j) + out << " " << IdPortal.Get(j); + out << '\n'; + } + + out << "CELL_TYPES " << nCells << '\n'; + for (vtkm::Id i = 0; i < nCells; ++i) + { + vtkm::Id shape = cellSet.GetCellShape(i); + out << shape << '\n'; + } +} + +void WriteVertexCells(std::ostream& out, const vtkm::cont::DataSet& dataSet) +{ + vtkm::Id nCells = dataSet.GetCoordinateSystem(0).GetNumberOfPoints(); + + out << "CELLS " << nCells << " " << nCells * 2 << '\n'; + for (int i = 0; i < nCells; i++) + { + out << "1 " << i << '\n'; + } + out << "CELL_TYPES " << nCells << '\n'; + for (int i = 0; i < nCells; i++) + { + out << vtkm::CELL_SHAPE_VERTEX << '\n'; + } +} + +void WritePointFields(std::ostream& out, const vtkm::cont::DataSet& dataSet) +{ + bool wrote_header = false; + for (vtkm::Id f = 0; f < dataSet.GetNumberOfFields(); f++) + { + const vtkm::cont::Field field = dataSet.GetField(f); + + if (field.GetAssociation() != vtkm::cont::Field::Association::POINTS) + { + continue; + } + + vtkm::Id npoints = field.GetNumberOfValues(); + int ncomps = field.GetData().GetNumberOfComponents(); + if (ncomps > 4) + { + continue; + } + + if (!wrote_header) + { + out << "POINT_DATA " << npoints << '\n'; + wrote_header = true; + } + + std::string typeName; + vtkm::cont::CastAndCall(field.GetData().ResetTypes(vtkm::TypeListAll{}), + GetDataTypeName(typeName)); + std::string name = field.GetName(); + for (auto& c : name) + { + if (std::isspace(c)) + { + c = '_'; + } + } + out << "SCALARS " << name << " " << typeName << " " << ncomps << '\n'; + out << "LOOKUP_TABLE default" << '\n'; + + vtkm::cont::CastAndCall(field.GetData().ResetTypes(vtkm::TypeListAll{}), + OutputFieldFunctor(out)); + } +} + +void WriteCellFields(std::ostream& out, const vtkm::cont::DataSet& dataSet) +{ + bool wrote_header = false; + for (vtkm::Id f = 0; f < dataSet.GetNumberOfFields(); f++) + { + const vtkm::cont::Field field = dataSet.GetField(f); + if (!field.IsFieldCell()) + { + continue; + } + + + vtkm::Id ncells = field.GetNumberOfValues(); + int ncomps = field.GetData().GetNumberOfComponents(); + if (ncomps > 4) + continue; + + if (!wrote_header) + { + out << "CELL_DATA " << ncells << '\n'; + wrote_header = true; + } + + std::string typeName; + vtkm::cont::CastAndCall(field.GetData().ResetTypes(vtkm::TypeListAll{}), + GetDataTypeName(typeName)); + + std::string name = field.GetName(); + for (auto& c : name) + { + if (std::isspace(c)) + { + c = '_'; + } + } + + out << "SCALARS " << name << " " << typeName << " " << ncomps << '\n'; + out << "LOOKUP_TABLE default" << '\n'; + + vtkm::cont::CastAndCall(field.GetData().ResetTypes(vtkm::TypeListAll{}), + OutputFieldFunctor(out)); + } +} + +void WriteDataSetAsPoints(std::ostream& out, const vtkm::cont::DataSet& dataSet) +{ + out << "DATASET UNSTRUCTURED_GRID" << '\n'; + WritePoints(out, dataSet); + WriteVertexCells(out, dataSet); +} + +template +void WriteDataSetAsUnstructured(std::ostream& out, + const vtkm::cont::DataSet& dataSet, + const CellSetType& cellSet) +{ + out << "DATASET UNSTRUCTURED_GRID" << '\n'; + WritePoints(out, dataSet); + WriteExplicitCells(out, cellSet); +} + +template +void WriteDataSetAsStructured(std::ostream& out, + const vtkm::cont::DataSet& dataSet, + const vtkm::cont::CellSetStructured& cellSet) +{ + ///\todo: support uniform/rectilinear + out << "DATASET STRUCTURED_GRID" << '\n'; + + auto pointDimensions = cellSet.GetPointDimensions(); + using VTraits = vtkm::VecTraits; + + out << "DIMENSIONS "; + out << VTraits::GetComponent(pointDimensions, 0) << " "; + out << (DIM > 1 ? VTraits::GetComponent(pointDimensions, 1) : 1) << " "; + out << (DIM > 2 ? VTraits::GetComponent(pointDimensions, 2) : 1) << " "; + + WritePoints(out, dataSet); +} + +void Write(std::ostream& out, const vtkm::cont::DataSet& dataSet, bool just_points = false) +{ + // The Paraview parser cannot handle scientific notation: + out << std::fixed; + out << "# vtk DataFile Version 3.0" << '\n'; + out << "vtk output" << '\n'; + out << "ASCII" << '\n'; + + if (just_points) + { + WriteDataSetAsPoints(out, dataSet); + WritePointFields(out, dataSet); + } + else + { + vtkm::cont::DynamicCellSet cellSet = dataSet.GetCellSet(); + if (cellSet.IsType>()) + { + WriteDataSetAsUnstructured(out, dataSet, cellSet.Cast>()); + } + else if (cellSet.IsType>()) + { + WriteDataSetAsStructured(out, dataSet, cellSet.Cast>()); + } + else if (cellSet.IsType>()) + { + WriteDataSetAsStructured(out, dataSet, cellSet.Cast>()); + } + else if (cellSet.IsType>()) + { + WriteDataSetAsStructured(out, dataSet, cellSet.Cast>()); + } + else if (cellSet.IsType>()) + { + // these function just like explicit cell sets + WriteDataSetAsUnstructured(out, dataSet, cellSet.Cast>()); + } + else + { + throw vtkm::cont::ErrorBadType("Could not determine type to write out."); + } + + WritePointFields(out, dataSet); + WriteCellFields(out, dataSet); + } +} + +} // anonymous namespace + +namespace vtkm +{ +namespace io +{ + +VTKDataSetWriter::VTKDataSetWriter(const char* fileName) + : FileName(fileName) +{ +} + +VTKDataSetWriter::VTKDataSetWriter(const std::string& fileName) + : FileName(fileName) +{ +} + +void VTKDataSetWriter::WriteDataSet(const vtkm::cont::DataSet& dataSet, bool just_points) const +{ + if (dataSet.GetNumberOfCoordinateSystems() < 1) + { + throw vtkm::cont::ErrorBadValue( + "DataSet has no coordinate system, which is not supported by VTK file format."); + } + try + { + std::ofstream fileStream(this->FileName.c_str(), std::fstream::trunc); + Write(fileStream, dataSet, just_points); + fileStream.close(); + } + catch (std::ofstream::failure& error) + { + throw vtkm::io::ErrorIO(error.what()); + } +} +} +} // namespace vtkm::io diff --git a/vtkm/io/VTKDataSetWriter.h b/vtkm/io/VTKDataSetWriter.h index 946ac5c3d..e666f0b7d 100644 --- a/vtkm/io/VTKDataSetWriter.h +++ b/vtkm/io/VTKDataSetWriter.h @@ -10,400 +10,22 @@ #ifndef vtk_m_io_DataSetWriter_h #define vtk_m_io_DataSetWriter_h -#include -#include - -#include -#include -#include #include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include +#include namespace vtkm { namespace io { -namespace detail -{ -struct OutputPointsFunctor -{ -private: - std::ostream& out; - - template - VTKM_CONT void Output(const PortalType& portal) const - { - for (vtkm::Id index = 0; index < portal.GetNumberOfValues(); index++) - { - const int VTKDims = 3; // VTK files always require 3 dims for points - - using ValueType = typename PortalType::ValueType; - using VecType = typename vtkm::VecTraits; - - const ValueType& value = portal.Get(index); - - vtkm::IdComponent numComponents = VecType::GetNumberOfComponents(value); - for (vtkm::IdComponent c = 0; c < numComponents && c < VTKDims; c++) - { - out << (c == 0 ? "" : " ") << VecType::GetComponent(value, c); - } - for (vtkm::IdComponent c = numComponents; c < VTKDims; c++) - { - out << " 0"; - } - out << '\n'; - } - } - -public: - VTKM_CONT - OutputPointsFunctor(std::ostream& o) - : out(o) - { - } - - template - VTKM_CONT void operator()(const vtkm::cont::ArrayHandle& array) const - { - this->Output(array.ReadPortal()); - } -}; - -struct OutputFieldFunctor -{ -private: - std::ostream& out; - - template - VTKM_CONT void Output(const PortalType& portal) const - { - for (vtkm::Id index = 0; index < portal.GetNumberOfValues(); index++) - { - using ValueType = typename PortalType::ValueType; - using VecType = typename vtkm::VecTraits; - - const ValueType& value = portal.Get(index); - - vtkm::IdComponent numComponents = VecType::GetNumberOfComponents(value); - for (vtkm::IdComponent c = 0; c < numComponents; c++) - { - out << (c == 0 ? "" : " ") << VecType::GetComponent(value, c); - } - out << '\n'; - } - } - -public: - VTKM_CONT - OutputFieldFunctor(std::ostream& o) - : out(o) - { - } - - template - VTKM_CONT void operator()(const vtkm::cont::ArrayHandle& array) const - { - this->Output(array.ReadPortal()); - } -}; - -class GetDataTypeName +struct VTKM_IO_EXPORT VTKDataSetWriter { public: - GetDataTypeName(std::string& name) - : Name(&name) - { - } + VTKM_CONT VTKDataSetWriter(const char* fileName); + VTKM_CONT VTKDataSetWriter(const std::string& fileName); - template - void operator()(const ArrayHandleType&) const - { - using DataType = typename vtkm::VecTraits::ComponentType; - *this->Name = vtkm::io::internal::DataTypeName::Name(); - } - -private: - std::string* Name; -}; - -} // namespace detail - -struct VTKDataSetWriter -{ -private: - static void WritePoints(std::ostream& out, const vtkm::cont::DataSet& dataSet) - { - ///\todo: support other coordinate systems - int cindex = 0; - auto cdata = dataSet.GetCoordinateSystem(cindex).GetData(); - - vtkm::Id npoints = cdata.GetNumberOfValues(); - out << "POINTS " << npoints << " " - << vtkm::io::internal::DataTypeName::Name() << " " << '\n'; - - detail::OutputPointsFunctor{ out }(cdata); - } - - template - static void WriteExplicitCells(std::ostream& out, const CellSetType& cellSet) - { - vtkm::Id nCells = cellSet.GetNumberOfCells(); - - vtkm::Id conn_length = 0; - for (vtkm::Id i = 0; i < nCells; ++i) - { - conn_length += 1 + cellSet.GetNumberOfPointsInCell(i); - } - - out << "CELLS " << nCells << " " << conn_length << '\n'; - - for (vtkm::Id i = 0; i < nCells; ++i) - { - vtkm::cont::ArrayHandle ids; - vtkm::Id nids = cellSet.GetNumberOfPointsInCell(i); - cellSet.GetIndices(i, ids); - out << nids; - auto IdPortal = ids.ReadPortal(); - for (int j = 0; j < nids; ++j) - out << " " << IdPortal.Get(j); - out << '\n'; - } - - out << "CELL_TYPES " << nCells << '\n'; - for (vtkm::Id i = 0; i < nCells; ++i) - { - vtkm::Id shape = cellSet.GetCellShape(i); - out << shape << '\n'; - } - } - - static void WriteVertexCells(std::ostream& out, const vtkm::cont::DataSet& dataSet) - { - vtkm::Id nCells = dataSet.GetCoordinateSystem(0).GetNumberOfPoints(); - - out << "CELLS " << nCells << " " << nCells * 2 << '\n'; - for (int i = 0; i < nCells; i++) - { - out << "1 " << i << '\n'; - } - out << "CELL_TYPES " << nCells << '\n'; - for (int i = 0; i < nCells; i++) - { - out << vtkm::CELL_SHAPE_VERTEX << '\n'; - } - } - - static void WritePointFields(std::ostream& out, const vtkm::cont::DataSet& dataSet) - { - bool wrote_header = false; - for (vtkm::Id f = 0; f < dataSet.GetNumberOfFields(); f++) - { - const vtkm::cont::Field field = dataSet.GetField(f); - - if (field.GetAssociation() != vtkm::cont::Field::Association::POINTS) - { - continue; - } - - vtkm::Id npoints = field.GetNumberOfValues(); - int ncomps = field.GetData().GetNumberOfComponents(); - if (ncomps > 4) - { - continue; - } - - if (!wrote_header) - { - out << "POINT_DATA " << npoints << '\n'; - wrote_header = true; - } - - std::string typeName; - vtkm::cont::CastAndCall(field.GetData().ResetTypes(TypeListAll{}), - detail::GetDataTypeName(typeName)); - std::string name = field.GetName(); - for (auto& c : name) - { - if (std::isspace(c)) - { - c = '_'; - } - } - out << "SCALARS " << name << " " << typeName << " " << ncomps << '\n'; - out << "LOOKUP_TABLE default" << '\n'; - - vtkm::cont::CastAndCall(field.GetData().ResetTypes(TypeListAll{}), - detail::OutputFieldFunctor(out)); - } - } - - static void WriteCellFields(std::ostream& out, const vtkm::cont::DataSet& dataSet) - { - bool wrote_header = false; - for (vtkm::Id f = 0; f < dataSet.GetNumberOfFields(); f++) - { - const vtkm::cont::Field field = dataSet.GetField(f); - if (!field.IsFieldCell()) - { - continue; - } - - - vtkm::Id ncells = field.GetNumberOfValues(); - int ncomps = field.GetData().GetNumberOfComponents(); - if (ncomps > 4) - continue; - - if (!wrote_header) - { - out << "CELL_DATA " << ncells << '\n'; - wrote_header = true; - } - - std::string typeName; - vtkm::cont::CastAndCall(field.GetData().ResetTypes(TypeListAll{}), - detail::GetDataTypeName(typeName)); - - std::string name = field.GetName(); - for (auto& c : name) - { - if (std::isspace(c)) - { - c = '_'; - } - } - - out << "SCALARS " << name << " " << typeName << " " << ncomps << '\n'; - out << "LOOKUP_TABLE default" << '\n'; - - vtkm::cont::CastAndCall(field.GetData().ResetTypes(TypeListAll{}), - detail::OutputFieldFunctor(out)); - } - } - - static void WriteDataSetAsPoints(std::ostream& out, const vtkm::cont::DataSet& dataSet) - { - out << "DATASET UNSTRUCTURED_GRID" << '\n'; - WritePoints(out, dataSet); - WriteVertexCells(out, dataSet); - } - - template - static void WriteDataSetAsUnstructured(std::ostream& out, - const vtkm::cont::DataSet& dataSet, - const CellSetType& cellSet) - { - out << "DATASET UNSTRUCTURED_GRID" << '\n'; - WritePoints(out, dataSet); - WriteExplicitCells(out, cellSet); - } - - template - static void WriteDataSetAsStructured(std::ostream& out, - const vtkm::cont::DataSet& dataSet, - const vtkm::cont::CellSetStructured& cellSet) - { - ///\todo: support uniform/rectilinear - out << "DATASET STRUCTURED_GRID" << '\n'; - - auto pointDimensions = cellSet.GetPointDimensions(); - using VTraits = vtkm::VecTraits; - - out << "DIMENSIONS "; - out << VTraits::GetComponent(pointDimensions, 0) << " "; - out << (DIM > 1 ? VTraits::GetComponent(pointDimensions, 1) : 1) << " "; - out << (DIM > 2 ? VTraits::GetComponent(pointDimensions, 2) : 1) << " "; - - WritePoints(out, dataSet); - } - - static void Write(std::ostream& out, const vtkm::cont::DataSet& dataSet, bool just_points = false) - { - // The Paraview parser cannot handle scientific notation: - out << std::fixed; - out << "# vtk DataFile Version 3.0" << '\n'; - out << "vtk output" << '\n'; - out << "ASCII" << '\n'; - - if (just_points) - { - WriteDataSetAsPoints(out, dataSet); - WritePointFields(out, dataSet); - } - else - { - vtkm::cont::DynamicCellSet cellSet = dataSet.GetCellSet(); - if (cellSet.IsType>()) - { - WriteDataSetAsUnstructured(out, dataSet, cellSet.Cast>()); - } - else if (cellSet.IsType>()) - { - WriteDataSetAsStructured(out, dataSet, cellSet.Cast>()); - } - else if (cellSet.IsType>()) - { - WriteDataSetAsStructured(out, dataSet, cellSet.Cast>()); - } - else if (cellSet.IsType>()) - { - WriteDataSetAsStructured(out, dataSet, cellSet.Cast>()); - } - else if (cellSet.IsType>()) - { - // these function just like explicit cell sets - WriteDataSetAsUnstructured(out, dataSet, cellSet.Cast>()); - } - else - { - throw vtkm::cont::ErrorBadType("Could not determine type to write out."); - } - - WritePointFields(out, dataSet); - WriteCellFields(out, dataSet); - } - } - -public: - VTKM_CONT - explicit VTKDataSetWriter(const std::string& filename) - : FileName(filename) - { - } - - VTKM_CONT - void WriteDataSet(const vtkm::cont::DataSet& dataSet, bool just_points = false) const - { - if (dataSet.GetNumberOfCoordinateSystems() < 1) - { - throw vtkm::cont::ErrorBadValue( - "DataSet has no coordinate system, which is not supported by VTK file format."); - } - try - { - std::ofstream fileStream(this->FileName.c_str(), std::fstream::trunc); - this->Write(fileStream, dataSet, just_points); - fileStream.close(); - } - catch (std::ofstream::failure& error) - { - throw vtkm::io::ErrorIO(error.what()); - } - } + VTKM_CONT void WriteDataSet(const vtkm::cont::DataSet& dataSet, bool just_points = false) const; private: std::string FileName; diff --git a/vtkm/io/internal/VTKDataSetTypes.h b/vtkm/io/internal/VTKDataSetTypes.h index 8210ff197..30e9ece20 100644 --- a/vtkm/io/internal/VTKDataSetTypes.h +++ b/vtkm/io/internal/VTKDataSetTypes.h @@ -11,6 +11,7 @@ #define vtk_m_io_internal_VTKDataSetTypes_h #include +#include #include #include @@ -238,4 +239,7 @@ inline void SelectTypeAndCall(DataType dtype, } } // namespace vtkm::io::internal +VTKM_BASIC_TYPE_VECTOR(vtkm::io::internal::ColorChannel8) +VTKM_BASIC_TYPE_VECTOR(vtkm::io::internal::DummyBitType) + #endif // vtk_m_io_internal_VTKDataSetTypes_h From dce576bd11a619fa9afed360516c2593989c722b Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Tue, 19 May 2020 14:31:14 -0600 Subject: [PATCH 4/5] Add BOVDataSetReader implementation to vtkm_io --- docs/changelog/vtk-io-in-library.md | 5 +- vtkm/io/BOVDataSetReader.cxx | 254 +++++++++++++++++++ vtkm/io/BOVDataSetReader.h | 223 +--------------- vtkm/io/CMakeLists.txt | 1 + vtkm/io/testing/UnitTestBOVDataSetReader.cxx | 1 + 5 files changed, 266 insertions(+), 218 deletions(-) create mode 100644 vtkm/io/BOVDataSetReader.cxx diff --git a/docs/changelog/vtk-io-in-library.md b/docs/changelog/vtk-io-in-library.md index d6616813e..37ed2f4f9 100644 --- a/docs/changelog/vtk-io-in-library.md +++ b/docs/changelog/vtk-io-in-library.md @@ -6,7 +6,8 @@ code into libraries. At this point, there is no reason why the VTK file reader/writer should be any different. Thus, `VTKDataSetReader`, `VTKDataSetWriter`, and several supporting -classes are now compiled into the `vtk_io` library. +classes are now compiled into the `vtkm_io` library. Also similarly updated +`BOVDataSetReader` for good measure. -As a side effect, code using VTK-m will need to link to `vtk_io` if they +As a side effect, code using VTK-m will need to link to `vtkm_io` if they are using any readers or writers. diff --git a/vtkm/io/BOVDataSetReader.cxx b/vtkm/io/BOVDataSetReader.cxx new file mode 100644 index 000000000..d66498692 --- /dev/null +++ b/vtkm/io/BOVDataSetReader.cxx @@ -0,0 +1,254 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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 + +#include +#include +#include + +#include + +namespace +{ + +enum class DataFormat +{ + ByteData, + ShortData, + IntegerData, + FloatData, + DoubleData +}; + +template +void ReadBuffer(const std::string& fName, const vtkm::Id& sz, std::vector& buff) +{ + FILE* fp = fopen(fName.c_str(), "rb"); + size_t readSize = static_cast(sz); + if (fp == nullptr) + { + throw vtkm::io::ErrorIO("Unable to open data file: " + fName); + } + buff.resize(readSize); + size_t nread = fread(&buff[0], sizeof(T), readSize, fp); + if (nread != readSize) + { + throw vtkm::io::ErrorIO("Data file read failed: " + fName); + } + fclose(fp); +} + +template +void ReadScalar(const std::string& fName, const vtkm::Id& nTuples, vtkm::cont::ArrayHandle& var) +{ + std::vector buff; + ReadBuffer(fName, nTuples, buff); + var.Allocate(nTuples); + auto writePortal = var.WritePortal(); + for (vtkm::Id i = 0; i < nTuples; i++) + { + writePortal.Set(i, buff[static_cast(i)]); + } +} + +template +void ReadVector(const std::string& fName, + const vtkm::Id& nTuples, + vtkm::cont::ArrayHandle>& var) +{ + std::vector buff; + ReadBuffer(fName, nTuples * 3, buff); + + var.Allocate(nTuples); + vtkm::Vec v; + auto writePortal = var.WritePortal(); + for (vtkm::Id i = 0; i < nTuples; i++) + { + v[0] = buff[static_cast(i * 3 + 0)]; + v[1] = buff[static_cast(i * 3 + 1)]; + v[2] = buff[static_cast(i * 3 + 2)]; + writePortal.Set(i, v); + } +} + +} // anonymous namespace + +namespace vtkm +{ +namespace io +{ + +BOVDataSetReader::BOVDataSetReader(const char* fileName) + : FileName(fileName) + , Loaded(false) + , DataSet() +{ +} + +BOVDataSetReader::BOVDataSetReader(const std::string& fileName) + : FileName(fileName) + , Loaded(false) + , DataSet() +{ +} + +const vtkm::cont::DataSet& BOVDataSetReader::ReadDataSet() +{ + try + { + this->LoadFile(); + } + catch (std::ifstream::failure& e) + { + std::string message("IO Error: "); + throw vtkm::io::ErrorIO(message + e.what()); + } + return this->DataSet; +} + +void BOVDataSetReader::LoadFile() +{ + if (this->Loaded) + return; + + std::ifstream stream(this->FileName); + if (stream.fail()) + throw vtkm::io::ErrorIO("Failed to open file: " + this->FileName); + + DataFormat dataFormat = DataFormat::ByteData; + std::string bovFile, line, token, options, variableName; + vtkm::Id numComponents = 1; + vtkm::Id3 dim; + vtkm::Vec3f origin(0, 0, 0); + vtkm::Vec3f spacing(1, 1, 1); + bool spacingSet = false; + + while (stream.good()) + { + std::getline(stream, line); + if (line.size() == 0 || line[0] == '#') + continue; + //std::cout<<"::"<> bovFile >> std::ws; + } + else if (token.find("DATA") != std::string::npos && token.find("SIZE") != std::string::npos) + { + strStream >> dim[0] >> dim[1] >> dim[2] >> std::ws; + } + else if (token.find("BRICK") != std::string::npos && token.find("ORIGIN") != std::string::npos) + { + strStream >> origin[0] >> origin[1] >> origin[2] >> std::ws; + } + + //DRP + else if (token.find("BRICK") != std::string::npos && token.find("SIZE") != std::string::npos) + { + strStream >> spacing[0] >> spacing[1] >> spacing[2] >> std::ws; + spacingSet = true; + } + else if (token.find("DATA") != std::string::npos && token.find("FORMAT") != std::string::npos) + { + std::string opt; + strStream >> opt >> std::ws; + if (opt.find("FLOAT") != std::string::npos || opt.find("REAL") != std::string::npos) + dataFormat = DataFormat::FloatData; + else if (opt.find("DOUBLE") != std::string::npos) + dataFormat = DataFormat::DoubleData; + else + throw vtkm::io::ErrorIO("Unsupported data type: " + token); + } + else if (token.find("DATA") != std::string::npos && + token.find("COMPONENTS") != std::string::npos) + { + strStream >> numComponents >> std::ws; + if (numComponents != 1 && numComponents != 3) + throw vtkm::io::ErrorIO("Unsupported number of components"); + } + else if (token.find("VARIABLE") != std::string::npos && + token.find("PALETTE") == std::string::npos) + { + strStream >> variableName >> std::ws; + if (variableName[0] == '"') + variableName = variableName.substr(1, variableName.size() - 2); + } + } + + if (spacingSet) + { + spacing[0] = (spacing[0]) / static_cast(dim[0] - 1); + spacing[1] = (spacing[1]) / static_cast(dim[1] - 1); + spacing[2] = (spacing[2]) / static_cast(dim[2] - 1); + } + + std::string fullPathDataFile; + std::size_t pos = FileName.rfind("/"); + if (pos != std::string::npos) + { + std::string baseDir; + baseDir = this->FileName.substr(0, pos); + fullPathDataFile = baseDir + "/" + bovFile; + } + else + fullPathDataFile = bovFile; + + + vtkm::cont::DataSetBuilderUniform dataSetBuilder; + vtkm::cont::DataSetFieldAdd dsf; + this->DataSet = dataSetBuilder.Create(dim, origin, spacing); + + vtkm::Id numTuples = dim[0] * dim[1] * dim[2]; + if (numComponents == 1) + { + if (dataFormat == DataFormat::FloatData) + { + vtkm::cont::ArrayHandle var; + ReadScalar(fullPathDataFile, numTuples, var); + dsf.AddPointField(this->DataSet, variableName, var); + } + else if (dataFormat == DataFormat::DoubleData) + { + vtkm::cont::ArrayHandle var; + ReadScalar(fullPathDataFile, numTuples, var); + dsf.AddPointField(this->DataSet, variableName, var); + } + } + else if (numComponents == 3) + { + if (dataFormat == DataFormat::FloatData) + { + vtkm::cont::ArrayHandle var; + ReadVector(fullPathDataFile, numTuples, var); + dsf.AddPointField(this->DataSet, variableName, var); + } + else if (dataFormat == DataFormat::DoubleData) + { + vtkm::cont::ArrayHandle var; + ReadVector(fullPathDataFile, numTuples, var); + dsf.AddPointField(this->DataSet, variableName, var); + } + } + + this->Loaded = true; +} +} +} // namespace vtkm::io diff --git a/vtkm/io/BOVDataSetReader.h b/vtkm/io/BOVDataSetReader.h index c8981a800..dbd2147c5 100644 --- a/vtkm/io/BOVDataSetReader.h +++ b/vtkm/io/BOVDataSetReader.h @@ -10,234 +10,25 @@ #ifndef vtk_m_io_BOVDataSetReader_h #define vtk_m_io_BOVDataSetReader_h -#include #include -#include -#include -#include + +#include namespace vtkm { namespace io { -class BOVDataSetReader +class VTKM_IO_EXPORT BOVDataSetReader { public: - BOVDataSetReader(const char* fileName) - : FileName(fileName) - , Loaded(false) - , DataSet() - { - } - BOVDataSetReader(const std::string& fileName) - : FileName(fileName) - , Loaded(false) - , DataSet() - { - } + VTKM_CONT BOVDataSetReader(const char* fileName); + VTKM_CONT BOVDataSetReader(const std::string& fileName); - const vtkm::cont::DataSet& ReadDataSet() - { - try - { - LoadFile(); - } - catch (std::ifstream::failure& e) - { - std::string message("IO Error: "); - throw vtkm::io::ErrorIO(message + e.what()); - } - return this->DataSet; - } + VTKM_CONT const vtkm::cont::DataSet& ReadDataSet(); private: - using DataFormat = enum { ByteData, ShortData, IntegerData, FloatData, DoubleData }; - - void LoadFile() - { - if (this->Loaded) - return; - - std::ifstream stream(this->FileName); - if (stream.fail()) - throw vtkm::io::ErrorIO("Failed to open file: " + this->FileName); - - DataFormat dataFormat = ByteData; - std::string bovFile, line, token, options, variableName; - vtkm::Id numComponents = 1; - vtkm::Id3 dim; - vtkm::Vec3f origin(0, 0, 0); - vtkm::Vec3f spacing(1, 1, 1); - bool spacingSet = false; - - while (stream.good()) - { - std::getline(stream, line); - if (line.size() == 0 || line[0] == '#') - continue; - //std::cout<<"::"<> bovFile >> std::ws; - } - else if (token.find("DATA") != std::string::npos && token.find("SIZE") != std::string::npos) - { - strStream >> dim[0] >> dim[1] >> dim[2] >> std::ws; - } - else if (token.find("BRICK") != std::string::npos && - token.find("ORIGIN") != std::string::npos) - { - strStream >> origin[0] >> origin[1] >> origin[2] >> std::ws; - } - - //DRP - else if (token.find("BRICK") != std::string::npos && token.find("SIZE") != std::string::npos) - { - strStream >> spacing[0] >> spacing[1] >> spacing[2] >> std::ws; - spacingSet = true; - } - else if (token.find("DATA") != std::string::npos && token.find("FORMAT") != std::string::npos) - { - std::string opt; - strStream >> opt >> std::ws; - if (opt.find("FLOAT") != std::string::npos || opt.find("REAL") != std::string::npos) - dataFormat = FloatData; - else if (opt.find("DOUBLE") != std::string::npos) - dataFormat = DoubleData; - else - throw vtkm::io::ErrorIO("Unsupported data type: " + token); - } - else if (token.find("DATA") != std::string::npos && - token.find("COMPONENTS") != std::string::npos) - { - strStream >> numComponents >> std::ws; - if (numComponents != 1 && numComponents != 3) - throw vtkm::io::ErrorIO("Unsupported number of components"); - } - else if (token.find("VARIABLE") != std::string::npos && - token.find("PALETTE") == std::string::npos) - { - strStream >> variableName >> std::ws; - if (variableName[0] == '"') - variableName = variableName.substr(1, variableName.size() - 2); - } - } - - if (spacingSet) - { - spacing[0] = (spacing[0]) / static_cast(dim[0] - 1); - spacing[1] = (spacing[1]) / static_cast(dim[1] - 1); - spacing[2] = (spacing[2]) / static_cast(dim[2] - 1); - } - - std::string fullPathDataFile; - std::size_t pos = FileName.rfind("/"); - if (pos != std::string::npos) - { - std::string baseDir; - baseDir = this->FileName.substr(0, pos); - fullPathDataFile = baseDir + "/" + bovFile; - } - else - fullPathDataFile = bovFile; - - - vtkm::cont::DataSetBuilderUniform dataSetBuilder; - vtkm::cont::DataSetFieldAdd dsf; - this->DataSet = dataSetBuilder.Create(dim, origin, spacing); - - vtkm::Id numTuples = dim[0] * dim[1] * dim[2]; - if (numComponents == 1) - { - if (dataFormat == FloatData) - { - vtkm::cont::ArrayHandle var; - ReadScalar(fullPathDataFile, numTuples, var); - dsf.AddPointField(this->DataSet, variableName, var); - } - else if (dataFormat == DoubleData) - { - vtkm::cont::ArrayHandle var; - ReadScalar(fullPathDataFile, numTuples, var); - dsf.AddPointField(this->DataSet, variableName, var); - } - } - else if (numComponents == 3) - { - if (dataFormat == FloatData) - { - vtkm::cont::ArrayHandle var; - ReadVector(fullPathDataFile, numTuples, var); - dsf.AddPointField(this->DataSet, variableName, var); - } - else if (dataFormat == DoubleData) - { - vtkm::cont::ArrayHandle var; - ReadVector(fullPathDataFile, numTuples, var); - dsf.AddPointField(this->DataSet, variableName, var); - } - } - - this->Loaded = true; - } - - template - void ReadBuffer(const std::string& fName, const vtkm::Id& sz, std::vector& buff) - { - FILE* fp = fopen(fName.c_str(), "rb"); - size_t readSize = static_cast(sz); - if (fp == nullptr) - throw vtkm::io::ErrorIO("Unable to open data file: " + fName); - buff.resize(readSize); - size_t nread = fread(&buff[0], sizeof(T), readSize, fp); - if (nread != readSize) - throw vtkm::io::ErrorIO("Data file read failed: " + fName); - fclose(fp); - } - - template - void ReadScalar(const std::string& fName, - const vtkm::Id& nTuples, - vtkm::cont::ArrayHandle& var) - { - std::vector buff; - ReadBuffer(fName, nTuples, buff); - var.Allocate(nTuples); - auto writePortal = var.WritePortal(); - for (vtkm::Id i = 0; i < nTuples; i++) - writePortal.Set(i, buff[(size_t)i]); - } - - template - void ReadVector(const std::string& fName, - const vtkm::Id& nTuples, - vtkm::cont::ArrayHandle>& var) - { - std::vector buff; - ReadBuffer(fName, nTuples * 3, buff); - - var.Allocate(nTuples); - vtkm::Vec v; - auto writePortal = var.WritePortal(); - for (vtkm::Id i = 0; i < nTuples; i++) - { - v[0] = buff[static_cast(i * 3 + 0)]; - v[1] = buff[static_cast(i * 3 + 1)]; - v[2] = buff[static_cast(i * 3 + 2)]; - writePortal.Set(i, v); - } - } + VTKM_CONT void LoadFile(); std::string FileName; bool Loaded; diff --git a/vtkm/io/CMakeLists.txt b/vtkm/io/CMakeLists.txt index 7b3e48d84..99db550d4 100644 --- a/vtkm/io/CMakeLists.txt +++ b/vtkm/io/CMakeLists.txt @@ -33,6 +33,7 @@ set(template_sources ) set(sources + BOVDataSetReader.cxx DecodePNG.cxx EncodePNG.cxx VTKDataSetReader.cxx diff --git a/vtkm/io/testing/UnitTestBOVDataSetReader.cxx b/vtkm/io/testing/UnitTestBOVDataSetReader.cxx index 833d23d85..f1af8c023 100644 --- a/vtkm/io/testing/UnitTestBOVDataSetReader.cxx +++ b/vtkm/io/testing/UnitTestBOVDataSetReader.cxx @@ -11,6 +11,7 @@ #include #include #include +#include namespace { From 05fcda6c7624e710af191460e47958354a91af2e Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Tue, 19 May 2020 15:04:08 -0600 Subject: [PATCH 5/5] Compile IO library with device compilers Because the IO classes use ArrayHandle, they have to be compiled with device compilers (even though they do not directly produce any device code). This is a dumb requirement, but it is a requirement nonetheless. --- vtkm/io/CMakeLists.txt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/vtkm/io/CMakeLists.txt b/vtkm/io/CMakeLists.txt index 99db550d4..6c9eaeedb 100644 --- a/vtkm/io/CMakeLists.txt +++ b/vtkm/io/CMakeLists.txt @@ -33,9 +33,16 @@ set(template_sources ) set(sources - BOVDataSetReader.cxx DecodePNG.cxx EncodePNG.cxx +) + +# TODO: None of these codes actually use a device. Rather, they access ArrayHandle, and we +# currently need to ensure that ArrayHandle is correctly compiled for all devices. This is +# kind of silly, so hopefully sometime in the future you will no longer need to compile for +# devices for ArrayHandle, and this requirement will go away. +set(device_sources + BOVDataSetReader.cxx VTKDataSetReader.cxx VTKDataSetReaderBase.cxx VTKDataSetWriter.cxx @@ -44,7 +51,7 @@ set(sources VTKStructuredGridReader.cxx VTKStructuredPointsReader.cxx VTKUnstructuredGridReader.cxx -) + ) vtkm_declare_headers( ${headers} @@ -54,6 +61,7 @@ vtkm_declare_headers( vtkm_library( NAME vtkm_io SOURCES ${sources} + DEVICE_SOURCES ${device_sources} HEADERS ${headers} TEMPLATE_SOURCES ${template_sources} )