/*========================================================================= Program: Visualization Toolkit Module: vtkXdmfHeavyData.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 "vtkXdmfHeavyData.h" #include "vtkCellArray.h" #include "vtkCellData.h" #include "vtkCellTypes.h" #include "vtkDataArrayRange.h" #include "vtkDataObjectTypes.h" #include "vtkDoubleArray.h" #include "vtkExtractSelection.h" #include "vtkFloatArray.h" #include "vtkIdTypeArray.h" #include "vtkInformation.h" #include "vtkMath.h" #include "vtkMergePoints.h" #include "vtkMultiBlockDataSet.h" #include "vtkNew.h" #include "vtkObjectFactory.h" #include "vtkPointData.h" #include "vtkPolyData.h" #include "vtkRectilinearGrid.h" #include "vtkSelection.h" #include "vtkSelectionNode.h" #include "vtkSmartPointer.h" #include "vtkStructuredData.h" #include "vtkStructuredGrid.h" #include "vtkUniformGrid.h" #include "vtkUnsignedCharArray.h" #include "vtkUnstructuredGrid.h" #include "vtkXdmfDataArray.h" #include "vtkXdmfReader.h" #include "vtkXdmfReaderInternal.h" #include #include #include #include #include #include "vtk_libxml2.h" #include VTKLIBXML2_HEADER(tree.h) #ifdef VTK_USE_64BIT_IDS typedef XdmfInt64 vtkXdmfIdType; #else typedef XdmfInt32 vtkXdmfIdType; #endif using namespace xdmf2; static void vtkScaleExtents(int in_exts[6], int out_exts[6], int stride[3]) { out_exts[0] = in_exts[0] / stride[0]; out_exts[1] = in_exts[1] / stride[0]; out_exts[2] = in_exts[2] / stride[1]; out_exts[3] = in_exts[3] / stride[1]; out_exts[4] = in_exts[4] / stride[2]; out_exts[5] = in_exts[5] / stride[2]; } static void vtkGetDims(int exts[6], int dims[3]) { dims[0] = exts[1] - exts[0] + 1; dims[1] = exts[3] - exts[2] + 1; dims[2] = exts[5] - exts[4] + 1; } //------------------------------------------------------------------------------ vtkXdmfHeavyData::vtkXdmfHeavyData(vtkXdmfDomain* domain, vtkAlgorithm* reader) { this->Reader = reader; this->Piece = 0; this->NumberOfPieces = 0; this->GhostLevels = 0; this->Extents[0] = this->Extents[2] = this->Extents[4] = 0; this->Extents[1] = this->Extents[3] = this->Extents[5] = -1; this->Domain = domain; this->Stride[0] = this->Stride[1] = this->Stride[2] = 1; } //------------------------------------------------------------------------------ vtkXdmfHeavyData::~vtkXdmfHeavyData() = default; //------------------------------------------------------------------------------ vtkDataObject* vtkXdmfHeavyData::ReadData() { if (this->Domain->GetNumberOfGrids() == 1) { // There's just 1 grid. Now in serial, this is all good. In parallel, we // need to be care: // 1. If the data is structured, we respect the update-extent and read // accordingly. // 2. If the data is unstructured, we read only on the root node. The user // can apply D3 or something to repartition the data. return this->ReadData(this->Domain->GetGrid(0)); } // this code is similar to ReadComposite() however we cannot use the same code // since the API for getting the children differs on the domain and the grid. bool distribute_leaf_nodes = this->NumberOfPieces > 1; XdmfInt32 numChildren = this->Domain->GetNumberOfGrids(); int number_of_leaf_nodes = 0; vtkMultiBlockDataSet* mb = vtkMultiBlockDataSet::New(); mb->SetNumberOfBlocks(numChildren); for (XdmfInt32 cc = 0; cc < numChildren; cc++) { XdmfGrid* xmfChild = this->Domain->GetGrid(cc); mb->GetMetaData(cc)->Set(vtkCompositeDataSet::NAME(), xmfChild->GetName()); bool child_is_leaf = (xmfChild->IsUniform() != 0); if (!child_is_leaf || !distribute_leaf_nodes || (number_of_leaf_nodes % this->NumberOfPieces) == this->Piece) { // it's possible that the data has way too many blocks, in which case the // reader didn't present the user with capabilities to select the actual // leaf node blocks as is the norm, instead only top-level grids were // shown. In that case we need to ensure that we skip grids the user // wanted us to skip explicitly. if (!this->Domain->GetGridSelection()->ArrayIsEnabled(xmfChild->GetName())) { continue; } vtkDataObject* childDO = this->ReadData(xmfChild); if (childDO) { mb->SetBlock(cc, childDO); childDO->Delete(); } } number_of_leaf_nodes += child_is_leaf ? 1 : 0; } return mb; } //------------------------------------------------------------------------------ vtkDataObject* vtkXdmfHeavyData::ReadData(XdmfGrid* xmfGrid, int blockId) { if (!xmfGrid || xmfGrid->GetGridType() == XDMF_GRID_UNSET) { // sanity check-ensure that the xmfGrid is valid. return nullptr; } XdmfInt32 gridType = (xmfGrid->GetGridType() & XDMF_GRID_MASK); if (gridType == XDMF_GRID_COLLECTION && xmfGrid->GetCollectionType() == XDMF_GRID_COLLECTION_TEMPORAL) { // grid is a temporal collection, pick the sub-grid with matching time and // process that. return this->ReadTemporalCollection(xmfGrid, blockId); } else if (gridType == XDMF_GRID_COLLECTION || gridType == XDMF_GRID_TREE) { return this->ReadComposite(xmfGrid); } // grid is a primitive grid, so read the data. return this->ReadUniformData(xmfGrid, blockId); } //------------------------------------------------------------------------------ vtkDataObject* vtkXdmfHeavyData::ReadComposite(XdmfGrid* xmfComposite) { assert(((xmfComposite->GetGridType() & XDMF_GRID_COLLECTION && xmfComposite->GetCollectionType() != XDMF_GRID_COLLECTION_TEMPORAL) || (xmfComposite->GetGridType() & XDMF_GRID_TREE)) && "Input must be a spatial collection or a tree"); vtkMultiBlockDataSet* multiBlock = vtkMultiBlockDataSet::New(); XdmfInt32 numChildren = xmfComposite->GetNumberOfChildren(); multiBlock->SetNumberOfBlocks(numChildren); bool distribute_leaf_nodes = (xmfComposite->GetGridType() & XDMF_GRID_COLLECTION && this->NumberOfPieces > 1); int number_of_leaf_nodes = 0; for (XdmfInt32 cc = 0; cc < numChildren; cc++) { XdmfGrid* xmfChild = xmfComposite->GetChild(cc); multiBlock->GetMetaData(cc)->Set(vtkCompositeDataSet::NAME(), xmfChild->GetName()); bool child_is_leaf = (xmfChild->IsUniform() != 0); if (!child_is_leaf || !distribute_leaf_nodes || (number_of_leaf_nodes % this->NumberOfPieces) == this->Piece) { vtkDataObject* childDO = this->ReadData(xmfChild, cc); if (childDO) { multiBlock->SetBlock(cc, childDO); childDO->Delete(); } } number_of_leaf_nodes += child_is_leaf ? 1 : 0; } return multiBlock; } //------------------------------------------------------------------------------ vtkDataObject* vtkXdmfHeavyData::ReadTemporalCollection( XdmfGrid* xmfTemporalCollection, int blockId) { assert(xmfTemporalCollection->GetGridType() & XDMF_GRID_COLLECTION && xmfTemporalCollection->GetCollectionType() == XDMF_GRID_COLLECTION_TEMPORAL && "Input must be a temporal collection"); // Find the children that are valid for the requested time (this->Time) and // read only those. // FIXME: I am tempted to remove support for supporting multiple matching // sub-grids for a time-step since that changes the composite data hierarchy // over time which makes it hard to use filters such as vtkExtractBlock etc. std::deque valid_children; for (XdmfInt32 cc = 0; cc < xmfTemporalCollection->GetNumberOfChildren(); cc++) { XdmfGrid* child = xmfTemporalCollection->GetChild(cc); if (child) { // ensure that we set correct epsilon for comparison // BUG #0013766. child->GetTime()->SetEpsilon(VTK_DBL_EPSILON); if (child->GetTime()->IsValid(this->Time, this->Time)) { valid_children.push_back(child); } } } // if no child matched this timestep, handle the case where the user didn't // specify any