/*========================================================================= Program: Visualization Toolkit Module: vtkXdmf3Writer.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 "vtkXdmf3Writer.h" #include "vtkDoubleArray.h" #include "vtkDataObject.h" #include "vtkDirectedGraph.h" #include "vtkImageData.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkMultiBlockDataSet.h" #include "vtkObjectFactory.h" #include "vtkPointSet.h" #include "vtkRectilinearGrid.h" #include "vtkStreamingDemandDrivenPipeline.h" #include "vtkStructuredGrid.h" #include "vtkXdmf3DataSet.h" #include "XdmfDomain.hpp" #include "XdmfGridCollection.hpp" #include "XdmfGridCollectionType.hpp" #include "XdmfHeavyDataWriter.hpp" #include "XdmfWriter.hpp" #include #include vtkObjectFactoryNewMacro(vtkXdmf3Writer); //============================================================================= class vtkXdmf3Writer::Internals { public: Internals() { } ~Internals() {}; void Init() { this->NumberOfTimeSteps = 1; this->CurrentTimeIndex = 0; this->Domain = XdmfDomain::New(); this->Writer = boost::shared_ptr(); this->AggregateDomain = boost::shared_ptr(); this->AggregateWriter = boost::shared_ptr(); this->DestinationGroups.push(this->Domain); this->Destination = this->DestinationGroups.top(); } void InitWriterName(const char *filename, unsigned int lightDataLimit) { this->Writer = XdmfWriter::New(filename); this->Writer->setLightDataLimit(lightDataLimit); this->Writer->getHeavyDataWriter()->setReleaseData(true); } void SwitchToTemporal() { shared_ptr dest = XdmfGridCollection::New(); dest->setType(XdmfGridCollectionType::Temporal()); this->DestinationGroups.push(dest); this->Destination = this->DestinationGroups.top(); this->Domain->insert(dest); } void WriteDataObject(vtkDataObject *dataSet, bool hasTime, double time, const char* name = 0) { if (!dataSet) { return; } switch (dataSet->GetDataObjectType()) { case VTK_MULTIBLOCK_DATA_SET: { shared_ptr group = XdmfGridCollection::New(); this->Destination->insert(group); this->DestinationGroups.push(group); this->Destination = this->DestinationGroups.top(); vtkMultiBlockDataSet *mbds = vtkMultiBlockDataSet::SafeDownCast(dataSet); for (unsigned int i = 0; i< mbds->GetNumberOfBlocks(); i++) { vtkDataObject *next = mbds->GetBlock(i); const char* blockName = mbds->GetMetaData(i)->Get(vtkCompositeDataSet::NAME()); this->WriteDataObject(next, hasTime, time, blockName); this->Domain->accept(this->Writer); } this->DestinationGroups.pop(); this->Destination = this->DestinationGroups.top(); break; } case VTK_STRUCTURED_POINTS: case VTK_IMAGE_DATA: case VTK_UNIFORM_GRID: { vtkXdmf3DataSet::VTKToXdmf( vtkImageData::SafeDownCast(dataSet), this->Destination.get(), hasTime, time, name); break; } case VTK_RECTILINEAR_GRID: {//VTK_RECTILINEAR_GRID is 3 vtkXdmf3DataSet::VTKToXdmf( vtkRectilinearGrid::SafeDownCast(dataSet), this->Destination.get(), hasTime, time, name); break; } case VTK_STRUCTURED_GRID: { vtkXdmf3DataSet::VTKToXdmf( vtkStructuredGrid::SafeDownCast(dataSet), this->Destination.get(), hasTime, time, name); break; } case VTK_POLY_DATA: case VTK_UNSTRUCTURED_GRID: { vtkXdmf3DataSet::VTKToXdmf( vtkPointSet::SafeDownCast(dataSet), this->Destination.get(), hasTime, time, name); break; } //case VTK_GRAPH: case VTK_DIRECTED_GRAPH: //case VTK_UNDIRECTED_GRAPH: { vtkXdmf3DataSet::VTKToXdmf( vtkDirectedGraph::SafeDownCast(dataSet), this->Destination.get(), hasTime, time, name); break; } default: { } } //this->Domain->accept(this->Writer); } boost::shared_ptr Domain; boost::shared_ptr Destination; boost::shared_ptr Writer; boost::shared_ptr AggregateDomain; boost::shared_ptr AggregateWriter; std::stack > DestinationGroups; int NumberOfTimeSteps; int CurrentTimeIndex; }; //============================================================================== //---------------------------------------------------------------------------- vtkXdmf3Writer::vtkXdmf3Writer() { this->FileName = nullptr; this->LightDataLimit = 100; this->WriteAllTimeSteps = false; this->TimeValues = nullptr; this->TimeValues = 0; this->InitWriters = true; this->Internal = new Internals(); this->SetNumberOfOutputPorts(0); } //---------------------------------------------------------------------------- vtkXdmf3Writer::~vtkXdmf3Writer() { this->SetFileName(nullptr); if (this->TimeValues) { this->TimeValues->Delete (); } } //---------------------------------------------------------------------------- void vtkXdmf3Writer::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); os << indent << "FileName: " << (this->FileName ? this->FileName : "(none)") << endl; os << indent << "LightDataLimit: " << this->LightDataLimit << endl; os << indent << "WriteAllTimeSteps: " << (this->WriteAllTimeSteps?"ON":"OFF") << endl; } //------------------------------------------------------------------------------ void vtkXdmf3Writer::SetInputData(vtkDataObject *input) { this->SetInputDataInternal(0,input); } //------------------------------------------------------------------------------ int vtkXdmf3Writer::Write() { // Make sure we have input. if (this->GetNumberOfInputConnections(0) < 1) { vtkErrorMacro("No input provided!"); return 0; } // always write even if the data hasn't changed this->Modified(); if (!this->Internal) { this->Internal = new Internals(); } this->Internal->Init(); this->Update(); delete this->Internal; this->Internal = nullptr; return 1; } //---------------------------------------------------------------------------- int vtkXdmf3Writer::RequestInformation( vtkInformation* vtkNotUsed(request), vtkInformationVector** inputVector, vtkInformationVector* vtkNotUsed(outputVector)) { // Does the input have timesteps? vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); if ( inInfo->Has(vtkStreamingDemandDrivenPipeline::TIME_STEPS()) ) { this->Internal->NumberOfTimeSteps = inInfo->Length( vtkStreamingDemandDrivenPipeline::TIME_STEPS() ); } else { this->Internal->NumberOfTimeSteps = 1; } return 1; } //---------------------------------------------------------------------------- int vtkXdmf3Writer::RequestUpdateExtent( vtkInformation* vtkNotUsed(request), vtkInformationVector** inputVector, vtkInformationVector* vtkNotUsed(outputVector)) { // get the requested update extent if(!this->TimeValues) { this->TimeValues = vtkDoubleArray::New(); vtkInformation *info = inputVector[0]->GetInformationObject(0); double *data = info->Get(vtkStreamingDemandDrivenPipeline::TIME_STEPS()); int len = info->Length(vtkStreamingDemandDrivenPipeline::TIME_STEPS()); this->TimeValues->SetNumberOfValues (len); if(data) { for (int i = 0; i < len; i ++) { this->TimeValues->SetValue (i, data[i]); } } } if (this->TimeValues && this->WriteAllTimeSteps) { //TODO:? Add a user ivar to specify a particular time, //which is different from current time. Can do it by updating //to a particular time then writing without writealltimesteps, //but that is annoying. if(this->TimeValues->GetPointer(0)) { double timeReq; timeReq = this->TimeValues->GetValue(this->Internal->CurrentTimeIndex); inputVector[0]->GetInformationObject(0)->Set( vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP(), timeReq); } } return 1; } //---------------------------------------------------------------------------- int vtkXdmf3Writer::RequestData( vtkInformation* request, vtkInformationVector** inputVector, vtkInformationVector* vtkNotUsed(outputVector)) { //Note: call Write() instead of RD() directly. //Write() does setup first before it calls RD(). if (!this->Internal->Domain) { return 1; } vtkInformation* inInfo = inputVector[0]->GetInformationObject (0); this->OriginalInput = vtkDataObject::SafeDownCast (inInfo->Get(vtkDataObject::DATA_OBJECT ())); this->WriteDataInternal (request); return 1; } //---------------------------------------------------------------------------- int vtkXdmf3Writer::GlobalContinueExecuting(int localContinueExecution) { return localContinueExecution; } //---------------------------------------------------------------------------- void vtkXdmf3Writer::WriteDataInternal (vtkInformation* request) { bool isTemporal = false; bool firstTimeStep = false; if (this->WriteAllTimeSteps && this->Internal->NumberOfTimeSteps > 1) { isTemporal = true; } if (isTemporal && this->Internal->CurrentTimeIndex == 0) { firstTimeStep = true; // Tell the pipeline to start looping. this->Internal->SwitchToTemporal(); request->Set(vtkStreamingDemandDrivenPipeline::CONTINUE_EXECUTING(), 1); } vtkInformation *inDataInfo = this->OriginalInput->GetInformation(); double dataT = 0; bool hasTime = false; if (inDataInfo->Has(vtkDataObject::DATA_TIME_STEP())) { dataT = this->OriginalInput->GetInformation() ->Get(vtkDataObject::DATA_TIME_STEP()); hasTime = true; } this->CheckParameters(); std::string testString(this->FileName); size_t tempLength = testString.length(); testString = testString.substr(0, (tempLength - 4)); std::string choppedFileName = testString; if (this->InitWriters == true) { if (this->NumberOfProcesses == 1) { this->Internal ->InitWriterName(this->FileName, this->LightDataLimit); } else { if(this->MyRank == 0) { this->Internal->AggregateDomain = XdmfDomain::New(); this->Internal->AggregateWriter = XdmfWriter::New(this->FileName); this->Internal->AggregateWriter ->setLightDataLimit(this->LightDataLimit); this->Internal->AggregateWriter->getHeavyDataWriter() ->setReleaseData(true); } // end if(this->MyRank == 0) std::string rankFileName = choppedFileName; rankFileName.append("."); rankFileName.append(std::to_string(this->NumberOfProcesses)); rankFileName.append("."); rankFileName.append(std::to_string(this->MyRank)); rankFileName.append(".xmf"); this->Internal->InitWriterName(rankFileName.c_str(), this->LightDataLimit); } this->InitWriters = false; } this->Internal->WriteDataObject(this->OriginalInput, hasTime, dataT); this->Internal->Domain->accept(this->Internal->Writer); int rankCount; if (this->NumberOfProcesses > 1 && this->MyRank == 0 && (!isTemporal || firstTimeStep)) { //write the root's top level meta file that refers to the satellites' //todo would be fancier to write out whole tree but xgrid to each //satellites contents. That would require gather calls to //determine how many leafs on each satellite and a rewrite. //XdmfGridCollections (aka XGrid) are xdmf3's way to cross reference shared_ptr aggregategroup = XdmfGridCollection::New(); aggregategroup->setType(XdmfGridCollectionType::Spatial()); //the structure is simple, one cross reference per top in each satellite for (rankCount = 0; rankCountNumberOfProcesses; rankCount++) { std::string rankFileName = choppedFileName; rankFileName.append("."); rankFileName.append(std::to_string(this->NumberOfProcesses)); rankFileName.append("."); rankFileName.append(std::to_string(rankCount)); rankFileName.append(".xmf"); std::string rankGridName = "/Xdmf/Domain/Grid[1]"; shared_ptr partController = XdmfGridController::New(rankFileName.c_str(), rankGridName.c_str()); //tricky part is we have to state what type we are referencing to. //otherwise readback fails. if (isTemporal) { shared_ptr grid = XdmfGridCollection::New(); grid->setType(XdmfGridCollectionType::Temporal()); grid->setGridController(partController); aggregategroup->insert(grid); continue; } switch (this->OriginalInput->GetDataObjectType()) { case VTK_STRUCTURED_POINTS: case VTK_IMAGE_DATA: case VTK_UNIFORM_GRID: { //todo: the content below is a don't care placeholder. //the specifics don't matter, only the grid type, //but libxdmf won't let you not specify with structured shared_ptr grid = XdmfRegularGrid::New(1,1,1, 0,0,0, 0.0,0.0,0.0); grid->setGridController(partController); aggregategroup->insert(grid); break; } case VTK_RECTILINEAR_GRID: { //todo: same as above shared_ptr xXCoords = XdmfArray::New(); shared_ptr xYCoords = XdmfArray::New(); shared_ptr xZCoords = XdmfArray::New(); shared_ptr grid = XdmfRectilinearGrid::New(xXCoords, xYCoords, xZCoords); grid->setGridController(partController); aggregategroup->insert(grid); break; } case VTK_STRUCTURED_GRID: { //todo: same as above shared_ptr xdims = XdmfArray::New(); shared_ptr grid = XdmfCurvilinearGrid::New(xdims); grid->setGridController(partController); aggregategroup->insert(grid); break; } case VTK_POLY_DATA: case VTK_UNSTRUCTURED_GRID: { shared_ptr grid = XdmfUnstructuredGrid::New(); grid->setGridController(partController); aggregategroup->insert(grid); break; } //case VTK_GRAPH: case VTK_DIRECTED_GRAPH: //case VTK_UNDIRECTED_GRAPH: { //todo: graph can't have a grid controller // shared_ptr grid = XdmfGraph::New(0); // //grid->setGridController(partController); // aggregategroup->insert(grid); break; } default: { shared_ptr grid = XdmfGridCollection::New(); grid->setType(XdmfGridCollectionType::Spatial()); grid->setGridController(partController); aggregategroup->insert(grid); break; } } //switch data object type } // foreach rank this->Internal->AggregateDomain->insert(aggregategroup); this->Internal->AggregateDomain ->accept(this->Internal->AggregateWriter); } //if need to write top level file this->Internal->CurrentTimeIndex++; if (this->Internal->CurrentTimeIndex >= this->Internal->NumberOfTimeSteps && this->WriteAllTimeSteps) { // Tell the pipeline to stop looping. request->Set(vtkStreamingDemandDrivenPipeline::CONTINUE_EXECUTING(), 0); this->Internal->CurrentTimeIndex = 0; } int localContinue = request->Get( vtkStreamingDemandDrivenPipeline::CONTINUE_EXECUTING()); if (this->GlobalContinueExecuting(localContinue) != localContinue) { // Some other node decided to stop the execution. assert (localContinue == 1); request->Set(vtkStreamingDemandDrivenPipeline::CONTINUE_EXECUTING(), 0); } } //---------------------------------------------------------------------------- int vtkXdmf3Writer::CheckParametersInternal (int _NumberOfProcesses, int _MyRank) { if (!this->FileName) { vtkErrorMacro("No filename specified."); return 0; } this->NumberOfProcesses = _NumberOfProcesses; this->MyRank = _MyRank; return 1; } //---------------------------------------------------------------------------- int vtkXdmf3Writer::CheckParameters () { return this->CheckParametersInternal(1, 0); }