/*========================================================================= Program: Visualization Toolkit Module: vtkXdmfReader.cxx Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkXdmfReader.h" #include "vtkXdmfHeavyData.h" #include "vtkXdmfReaderInternal.h" #include "vtkCharArray.h" #include "vtkCompositeDataPipeline.h" #include "vtkDataObjectTypes.h" #include "vtkDataSet.h" #include "vtkExtentTranslator.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkMultiBlockDataSet.h" #include "vtkNew.h" #include "vtkObjectFactory.h" #include "vtkXMLParser.h" #include "vtksys/FStream.hxx" vtkCxxSetObjectMacro(vtkXdmfReader, InputArray, vtkCharArray); //============================================================================ class vtkXdmfReaderTester : public vtkXMLParser { public: vtkTypeMacro(vtkXdmfReaderTester, vtkXMLParser); static vtkXdmfReaderTester* New(); int TestReadFile() { this->Valid = 0; if (!this->FileName) { return 0; } vtksys::ifstream inFile(this->FileName); if (!inFile) { return 0; } this->SetStream(&inFile); this->Done = 0; this->Parse(); if (this->Done && this->Valid) { return 1; } return 0; } void StartElement(const char* name, const char**) override { this->Done = 1; if (strcmp(name, "Xdmf") == 0) { this->Valid = 1; } } protected: vtkXdmfReaderTester() { this->Valid = 0; this->Done = 0; } private: void ReportStrayAttribute(const char*, const char*, const char*) override {} void ReportMissingAttribute(const char*, const char*) override {} void ReportBadAttribute(const char*, const char*, const char*) override {} void ReportUnknownElement(const char*) override {} void ReportXmlParseError() override {} int ParsingComplete() override { return this->Done; } int Valid; int Done; vtkXdmfReaderTester(const vtkXdmfReaderTester&) = delete; void operator=(const vtkXdmfReaderTester&) = delete; }; vtkStandardNewMacro(vtkXdmfReaderTester); vtkStandardNewMacro(vtkXdmfReader); //------------------------------------------------------------------------------ vtkXdmfReader::vtkXdmfReader() { this->DomainName = nullptr; this->Stride[0] = this->Stride[1] = this->Stride[2] = 1; this->XdmfDocument = new vtkXdmfDocument(); this->LastTimeIndex = 0; this->SILUpdateStamp = 0; this->PointArraysCache = new vtkXdmfArraySelection; this->CellArraysCache = new vtkXdmfArraySelection; this->GridsCache = new vtkXdmfArraySelection; this->SetsCache = new vtkXdmfArraySelection; this->FileName = nullptr; this->ReadFromInputString = false; this->InputString = nullptr; this->InputStringLength = 0; this->InputArray = nullptr; this->SetNumberOfInputPorts(0); } //------------------------------------------------------------------------------ vtkXdmfReader::~vtkXdmfReader() { this->SetDomainName(nullptr); delete this->XdmfDocument; this->XdmfDocument = nullptr; delete this->PointArraysCache; delete this->CellArraysCache; delete this->GridsCache; delete this->SetsCache; this->ClearDataSetCache(); this->SetFileName(nullptr); delete[] this->InputString; } //------------------------------------------------------------------------------ void vtkXdmfReader::SetInputString(const char* in) { int len = 0; if (in != nullptr) { len = static_cast(strlen(in)); } this->SetInputString(in, len); } //------------------------------------------------------------------------------ void vtkXdmfReader::SetBinaryInputString(const char* in, int len) { this->SetInputString(in, len); } //------------------------------------------------------------------------------ void vtkXdmfReader::SetInputString(const char* in, int len) { if (this->Debug) { vtkDebugMacro(<< "SetInputString len: " << len << " in: " << (in ? in : "(null)")); } if (this->InputString && in && strncmp(in, this->InputString, len) == 0) { return; } delete[] this->InputString; if (in && len > 0) { // Add a nullptr terminator so that GetInputString // callers (from wrapped languages) get a valid // C string in *ALL* cases... // this->InputString = new char[len + 1]; memcpy(this->InputString, in, len); this->InputString[len] = 0; this->InputStringLength = len; } else { this->InputString = nullptr; this->InputStringLength = 0; } this->Modified(); } //------------------------------------------------------------------------------ int vtkXdmfReader::CanReadFile(const char* filename) { vtkXdmfReaderTester* tester = vtkXdmfReaderTester::New(); tester->SetFileName(filename); int res = tester->TestReadFile(); tester->Delete(); return res; } //------------------------------------------------------------------------------ int vtkXdmfReader::FillOutputPortInformation(int, vtkInformation* info) { info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkDataObject"); return 1; } //------------------------------------------------------------------------------ vtkTypeBool vtkXdmfReader::ProcessRequest( vtkInformation* request, vtkInformationVector** inputVector, vtkInformationVector* outputVector) { // create the output if (request->Has(vtkDemandDrivenPipeline::REQUEST_DATA_OBJECT())) { return this->RequestDataObjectInternal(outputVector); } return this->Superclass::ProcessRequest(request, inputVector, outputVector); } //------------------------------------------------------------------------------ bool vtkXdmfReader::PrepareDocument() { // Calling this method repeatedly is okay. It does work only when something // has changed. if (this->GetReadFromInputString()) { const char* data = nullptr; unsigned int data_length = 0; if (this->InputArray) { data = this->InputArray->GetPointer(0); data_length = static_cast( this->InputArray->GetNumberOfTuples() * this->InputArray->GetNumberOfComponents()); } else if (this->InputString) { data = this->InputString; data_length = this->InputStringLength; } else { vtkErrorMacro("No input string specified"); return false; } if (!this->XdmfDocument->ParseString(data, data_length)) { vtkErrorMacro("Failed to parse xmf."); return false; } } else { // Parse the file... if (!this->FileName) { vtkErrorMacro("File name not set"); return false; } // First make sure the file exists. This prevents an empty file // from being created on older compilers. if (!vtksys::SystemTools::FileExists(this->FileName)) { vtkErrorMacro("Error opening file " << this->FileName); return false; } if (!this->XdmfDocument->Parse(this->FileName)) { vtkErrorMacro("Failed to parse xmf file: " << this->FileName); return false; } } if (this->DomainName) { if (!this->XdmfDocument->SetActiveDomain(this->DomainName)) { vtkErrorMacro("Invalid domain: " << this->DomainName); return false; } } else { this->XdmfDocument->SetActiveDomain(static_cast(0)); } if (this->XdmfDocument->GetActiveDomain() && this->XdmfDocument->GetActiveDomain()->GetSIL()->GetMTime() > this->GetMTime()) { this->SILUpdateStamp++; } this->LastTimeIndex = 0; // reset time index when the file changes. return (this->XdmfDocument->GetActiveDomain() != nullptr); } //------------------------------------------------------------------------------ int vtkXdmfReader::RequestDataObjectInternal(vtkInformationVector* outputVector) { if (!this->PrepareDocument()) { return 0; } int vtk_type = this->XdmfDocument->GetActiveDomain()->GetVTKDataType(); if (this->XdmfDocument->GetActiveDomain()->GetSetsSelection()->GetNumberOfArrays() > 0) { // if the data has any sets, then we are forced to using multiblock. vtk_type = VTK_MULTIBLOCK_DATA_SET; } vtkDataObject* output = vtkDataObject::GetData(outputVector, 0); if (!output || output->GetDataObjectType() != vtk_type) { output = vtkDataObjectTypes::NewDataObject(vtk_type); outputVector->GetInformationObject(0)->Set(vtkDataObject::DATA_OBJECT(), output); this->GetOutputPortInformation(0)->Set( vtkDataObject::DATA_EXTENT_TYPE(), output->GetExtentType()); output->Delete(); } return 1; } //------------------------------------------------------------------------------ int vtkXdmfReader::RequestInformation( vtkInformation*, vtkInformationVector**, vtkInformationVector* outputVector) { if (!this->PrepareDocument()) { return 0; } // Pass any cached user-selections to the active domain. this->PassCachedSelections(); vtkInformation* outInfo = outputVector->GetInformationObject(0); vtkXdmfDomain* domain = this->XdmfDocument->GetActiveDomain(); // * Publish the fact that this reader can satisfy any piece request. outInfo->Set(CAN_HANDLE_PIECE_REQUEST(), 1); this->LastTimeIndex = this->ChooseTimeStep(outInfo); // Set the requested time-step on the domain. Thus, now when we go to get // information, we can (ideally) get information about that time-step. // this->XdmfDocument->GetActiveDomain()->SetTimeStep(this->LastTimeIndex); // * If producing structured dataset put information about whole extents etc. if (domain->GetNumberOfGrids() == 1 && domain->IsStructured(domain->GetGrid(0)) && domain->GetSetsSelection()->GetNumberOfArrays() == 0) { xdmf2::XdmfGrid* xmfGrid = domain->GetGrid(0); // just in the case the top-level grid is a temporal collection, then pick // the sub-grid to fetch the extents etc. xmfGrid = domain->GetGrid(xmfGrid, domain->GetTimeForIndex(this->LastTimeIndex)); int whole_extent[6]; if (domain->GetWholeExtent(xmfGrid, whole_extent)) { // re-scale the whole_extent using the stride. whole_extent[1] /= this->Stride[0]; whole_extent[3] /= this->Stride[1]; whole_extent[5] /= this->Stride[2]; outInfo->Set(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), whole_extent, 6); } double origin[3]; double spacing[3]; if (domain->GetOriginAndSpacing(xmfGrid, origin, spacing)) { spacing[0] *= this->Stride[0]; spacing[1] *= this->Stride[1]; spacing[2] *= this->Stride[2]; outInfo->Set(vtkDataObject::ORIGIN(), origin, 3); outInfo->Set(vtkDataObject::SPACING(), spacing, 3); } } // * Publish the SIL which provides information about the grid hierarchy. outInfo->Set(vtkDataObject::SIL(), domain->GetSIL()); // * Publish time information. const std::map& ts = domain->GetTimeStepsRev(); std::vector time_steps(ts.size()); std::map::const_iterator it = ts.begin(); for (int i = 0; it != ts.end(); i++, ++it) { time_steps[i] = it->second; } if (!time_steps.empty()) { outInfo->Set(vtkStreamingDemandDrivenPipeline::TIME_STEPS(), time_steps.data(), static_cast(time_steps.size())); double timeRange[2]; timeRange[0] = time_steps.front(); timeRange[1] = time_steps.back(); outInfo->Set(vtkStreamingDemandDrivenPipeline::TIME_RANGE(), timeRange, 2); } return 1; } //------------------------------------------------------------------------------ int vtkXdmfReader::RequestData( vtkInformation*, vtkInformationVector**, vtkInformationVector* outputVector) { if (!this->PrepareDocument()) { return 0; } vtkInformation* outInfo = outputVector->GetInformationObject(0); vtkDataObject* output = vtkDataObject::GetData(outInfo); // * Collect information about what part of the data is requested. unsigned int updatePiece = 0; unsigned int updateNumPieces = 1; int ghost_levels = 0; if (outInfo->Has(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER()) && outInfo->Has(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES())) { updatePiece = static_cast( outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER())); updateNumPieces = static_cast( outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES())); } if (outInfo->Has(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS())) { ghost_levels = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS()); } // will be set for structured datasets only. int update_extent[6] = { 0, -1, 0, -1, 0, -1 }; int zero_extent[6] = { 0, -1, 0, -1, 0, -1 }; bool generateGhostArray = false; if (output->GetExtentType() == VTK_3D_EXTENT && outInfo->Has(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT())) { outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), update_extent); if (outInfo->Has(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT())) { int wholeExtent[6]; outInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), wholeExtent); vtkNew et; et->SetWholeExtent(wholeExtent); et->SetPiece(updatePiece); et->SetNumberOfPieces(updateNumPieces); et->SetGhostLevel(ghost_levels); et->PieceToExtent(); et->GetExtent(update_extent); if (ghost_levels > 0) { et->SetGhostLevel(0); et->PieceToExtent(); et->GetExtent(zero_extent); generateGhostArray = true; } } } this->LastTimeIndex = this->ChooseTimeStep(outInfo); if (this->LastTimeIndex == 0) { this->ClearDataSetCache(); } vtkXdmfHeavyData dataReader(this->XdmfDocument->GetActiveDomain(), this); dataReader.Piece = updatePiece; dataReader.NumberOfPieces = updateNumPieces; dataReader.GhostLevels = ghost_levels; dataReader.Extents[0] = update_extent[0] * this->Stride[0]; dataReader.Extents[1] = update_extent[1] * this->Stride[0]; dataReader.Extents[2] = update_extent[2] * this->Stride[1]; dataReader.Extents[3] = update_extent[3] * this->Stride[1]; dataReader.Extents[4] = update_extent[4] * this->Stride[2]; dataReader.Extents[5] = update_extent[5] * this->Stride[2]; dataReader.Stride[0] = this->Stride[0]; dataReader.Stride[1] = this->Stride[1]; dataReader.Stride[2] = this->Stride[2]; dataReader.Time = this->XdmfDocument->GetActiveDomain()->GetTimeForIndex(this->LastTimeIndex); vtkDataObject* data = dataReader.ReadData(); if (!data) { vtkErrorMacro("Failed to read data."); return 0; } if (!output->IsA(data->GetClassName())) { // BUG #0013766: Just in case the data type expected doesn't match the // produced data type, we should print a warning. vtkWarningMacro("Data type generated (" << data->GetClassName() << ") " "does not match data type expected (" << output->GetClassName() << "). " "Reader may not produce valid data."); } output->ShallowCopy(data); data->Delete(); if (this->LastTimeIndex < this->XdmfDocument->GetActiveDomain()->GetTimeSteps().size()) { double time = this->XdmfDocument->GetActiveDomain()->GetTimeForIndex(this->LastTimeIndex); output->GetInformation()->Set(vtkDataObject::DATA_TIME_STEP(), time); } if (generateGhostArray) { vtkDataSet* output_tmp = vtkDataSet::SafeDownCast(output); if (output_tmp != nullptr) { output_tmp->GenerateGhostArray(zero_extent); } } return 1; } //------------------------------------------------------------------------------ int vtkXdmfReader::ChooseTimeStep(vtkInformation* outInfo) { if (outInfo->Has(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP())) { // we do not support multiple timestep requests. double time = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP()); return this->XdmfDocument->GetActiveDomain()->GetIndexForTime(time); } // if no timestep was requested, just return what we read last. return this->LastTimeIndex; } //------------------------------------------------------------------------------ vtkXdmfArraySelection* vtkXdmfReader::GetPointArraySelection() { return this->XdmfDocument->GetActiveDomain() ? this->XdmfDocument->GetActiveDomain()->GetPointArraySelection() : this->PointArraysCache; } //------------------------------------------------------------------------------ vtkXdmfArraySelection* vtkXdmfReader::GetCellArraySelection() { return this->XdmfDocument->GetActiveDomain() ? this->XdmfDocument->GetActiveDomain()->GetCellArraySelection() : this->CellArraysCache; } //------------------------------------------------------------------------------ vtkXdmfArraySelection* vtkXdmfReader::GetGridSelection() { return this->XdmfDocument->GetActiveDomain() ? this->XdmfDocument->GetActiveDomain()->GetGridSelection() : this->GridsCache; } //------------------------------------------------------------------------------ vtkXdmfArraySelection* vtkXdmfReader::GetSetsSelection() { return this->XdmfDocument->GetActiveDomain() ? this->XdmfDocument->GetActiveDomain()->GetSetsSelection() : this->SetsCache; } //------------------------------------------------------------------------------ int vtkXdmfReader::GetNumberOfGrids() { return this->GetGridSelection()->GetNumberOfArrays(); } //------------------------------------------------------------------------------ void vtkXdmfReader::SetGridStatus(const char* gridname, int status) { this->GetGridSelection()->SetArrayStatus(gridname, status != 0); this->Modified(); } //------------------------------------------------------------------------------ int vtkXdmfReader::GetGridStatus(const char* arrayname) { return this->GetGridSelection()->GetArraySetting(arrayname); } //------------------------------------------------------------------------------ const char* vtkXdmfReader::GetGridName(int index) { return this->GetGridSelection()->GetArrayName(index); } //------------------------------------------------------------------------------ int vtkXdmfReader::GetNumberOfPointArrays() { return this->GetPointArraySelection()->GetNumberOfArrays(); } //------------------------------------------------------------------------------ void vtkXdmfReader::SetPointArrayStatus(const char* arrayname, int status) { this->GetPointArraySelection()->SetArrayStatus(arrayname, status != 0); this->Modified(); } //------------------------------------------------------------------------------ int vtkXdmfReader::GetPointArrayStatus(const char* arrayname) { return this->GetPointArraySelection()->GetArraySetting(arrayname); } //------------------------------------------------------------------------------ const char* vtkXdmfReader::GetPointArrayName(int index) { return this->GetPointArraySelection()->GetArrayName(index); } //------------------------------------------------------------------------------ int vtkXdmfReader::GetNumberOfCellArrays() { return this->GetCellArraySelection()->GetNumberOfArrays(); } //------------------------------------------------------------------------------ void vtkXdmfReader::SetCellArrayStatus(const char* arrayname, int status) { this->GetCellArraySelection()->SetArrayStatus(arrayname, status != 0); this->Modified(); } //------------------------------------------------------------------------------ int vtkXdmfReader::GetCellArrayStatus(const char* arrayname) { return this->GetCellArraySelection()->GetArraySetting(arrayname); } //------------------------------------------------------------------------------ const char* vtkXdmfReader::GetCellArrayName(int index) { return this->GetCellArraySelection()->GetArrayName(index); } //------------------------------------------------------------------------------ int vtkXdmfReader::GetNumberOfSets() { return this->GetSetsSelection()->GetNumberOfArrays(); } //------------------------------------------------------------------------------ void vtkXdmfReader::SetSetStatus(const char* arrayname, int status) { this->GetSetsSelection()->SetArrayStatus(arrayname, status != 0); this->Modified(); } //------------------------------------------------------------------------------ int vtkXdmfReader::GetSetStatus(const char* arrayname) { return this->GetSetsSelection()->GetArraySetting(arrayname); } //------------------------------------------------------------------------------ const char* vtkXdmfReader::GetSetName(int index) { return this->GetSetsSelection()->GetArrayName(index); } //------------------------------------------------------------------------------ void vtkXdmfReader::PassCachedSelections() { if (!this->XdmfDocument->GetActiveDomain()) { return; } this->GetPointArraySelection()->Merge(*this->PointArraysCache); this->GetCellArraySelection()->Merge(*this->CellArraysCache); this->GetGridSelection()->Merge(*this->GridsCache); this->GetSetsSelection()->Merge(*this->SetsCache); // Clear the cache. this->PointArraysCache->clear(); this->CellArraysCache->clear(); this->GridsCache->clear(); this->SetsCache->clear(); } //------------------------------------------------------------------------------ void vtkXdmfReader::PrintSelf(ostream& os, vtkIndent indent) { os << indent << "ReadFromInputString: " << (this->ReadFromInputString ? "On\n" : "Off\n"); if (this->InputArray) { os << indent << "Input Array: " << "\n"; this->InputArray->PrintSelf(os, indent.GetNextIndent()); } else { os << indent << "Input String: (None)\n"; } this->Superclass::PrintSelf(os, indent); } //------------------------------------------------------------------------------ vtkGraph* vtkXdmfReader::GetSIL() { if (vtkXdmfDomain* domain = this->XdmfDocument->GetActiveDomain()) { return domain->GetSIL(); } return nullptr; } //------------------------------------------------------------------------------ void vtkXdmfReader::ClearDataSetCache() { XdmfReaderCachedData::iterator it = this->DataSetCache.begin(); while (it != this->DataSetCache.end()) { if (it->second.dataset != nullptr) { it->second.dataset->Delete(); } ++it; } this->DataSetCache.clear(); } //------------------------------------------------------------------------------ vtkXdmfReader::XdmfReaderCachedData& vtkXdmfReader::GetDataSetCache() { return this->DataSetCache; }