1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkXMLPartitionedDataSetWriter.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 #include "vtkXMLPartitionedDataSetWriter.h"
16 
17 #include "vtkErrorCode.h"
18 #include "vtkInformation.h"
19 #include "vtkLogger.h"
20 #include "vtkMultiProcessController.h"
21 #include "vtkNew.h"
22 #include "vtkObjectFactory.h"
23 #include "vtkPartitionedDataSet.h"
24 #include "vtkPartitionedDataSetCollection.h"
25 #include "vtkXMLCompositeDataSetWriterHelper.h"
26 #include "vtkXMLDataElement.h"
27 #include "vtkXMLDataWriterHelper.h"
28 
29 #include <vtksys/SystemTools.hxx>
30 
31 #include <cassert>
32 #include <map>
33 #include <memory>
34 
35 vtkStandardNewMacro(vtkXMLPartitionedDataSetWriter);
36 //----------------------------------------------------------------------------
37 vtkXMLPartitionedDataSetWriter::vtkXMLPartitionedDataSetWriter() = default;
38 
39 //----------------------------------------------------------------------------
40 vtkXMLPartitionedDataSetWriter::~vtkXMLPartitionedDataSetWriter() = default;
41 
42 //----------------------------------------------------------------------------
SetInputData(vtkPartitionedDataSet * pd)43 void vtkXMLPartitionedDataSetWriter::SetInputData(vtkPartitionedDataSet* pd)
44 {
45   this->SetInputDataObject(pd);
46 }
47 
48 //----------------------------------------------------------------------------
FillInputPortInformation(int,vtkInformation * info)49 int vtkXMLPartitionedDataSetWriter::FillInputPortInformation(int, vtkInformation* info)
50 {
51   info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPartitionedDataSet");
52   return 1;
53 }
54 
55 //----------------------------------------------------------------------------
RequestData(vtkInformation *,vtkInformationVector ** inputVector,vtkInformationVector *)56 int vtkXMLPartitionedDataSetWriter::RequestData(
57   vtkInformation*, vtkInformationVector** inputVector, vtkInformationVector*)
58 {
59   vtkLogScopeF(TRACE, "RequestData ('%s')", this->FileName);
60   this->SetErrorCode(vtkErrorCode::UnknownError);
61 
62   if (this->WriteToOutputString)
63   {
64     vtkErrorMacro("This writer does not support writing to string yet.");
65     return 0;
66   }
67 
68   if (!this->FileName || this->FileName[0] == '\0')
69   {
70     this->SetErrorCode(vtkErrorCode::NoFileNameError);
71     vtkErrorMacro("Filename cannot be empty!");
72     return 0;
73   }
74 
75   auto controller = this->GetController();
76 
77   auto inputPDS = vtkPartitionedDataSet::GetData(inputVector[0], 0);
78   assert(inputPDS != nullptr);
79 
80   this->UpdateProgress(0.0);
81 
82   std::string path, filename, artifactsDir;
83   std::tie(path, filename, artifactsDir) = vtkXMLWriter2::SplitFileName(this->FileName);
84   vtkLogF(TRACE, "Filename components(path='%s', filename='%s', artifactsDir='%s')", path.c_str(),
85     filename.c_str(), artifactsDir.c_str());
86   if (!this->MakeDirectory(path))
87   {
88     this->SetErrorCode(vtkErrorCode::OutOfDiskSpaceError);
89     vtkErrorMacro("Failed to create directory '" << path.c_str() << "'.");
90     return 0;
91   }
92 
93   // we intentionally don't add path as an artifact to cleanup if write fails.
94   // this->AddArtifact(path, true);
95 
96   const auto absoluteArtifactsDir = path + "/" + artifactsDir;
97   if (!this->MakeDirectory(absoluteArtifactsDir))
98   {
99     this->SetErrorCode(vtkErrorCode::OutOfDiskSpaceError);
100     vtkErrorMacro("Failed to create directory '" << absoluteArtifactsDir.c_str() << "'.");
101     return 0;
102   }
103   this->AddRootArtifact(absoluteArtifactsDir, /*isDir*/ true);
104 
105   // note: localDataSets may have nullptrs.
106   auto localDataSets =
107     vtkCompositeDataSet::GetDataSets<vtkDataObject>(inputPDS, /*preserveNull=*/true);
108   const int localOffset =
109     vtkXMLWriter2::ExclusiveScanSum(controller, static_cast<int>(localDataSets.size()));
110 
111   // note: localFilenames may have empty strings.
112   std::vector<std::string> localFilenames;
113 
114   vtkNew<vtkXMLCompositeDataSetWriterHelper> helper;
115   helper->SetWriter(this);
116   const auto filenameNoExt = vtksys::SystemTools::GetFilenameWithoutLastExtension(filename);
117   for (size_t cc = 0; cc < localDataSets.size(); ++cc)
118   {
119     const auto prefix = artifactsDir + "/" + filenameNoExt + "_" + std::to_string(localOffset + cc);
120     auto fname = helper->WriteDataSet(path, prefix, localDataSets[cc]);
121     localFilenames.push_back(fname);
122     if (!fname.empty())
123     {
124       this->AddArtifact(fname);
125     }
126   }
127 
128   // pass written filenames to root node. allFilenames is non empty only on root node.
129   std::vector<std::string> allFilenames = vtkXMLWriter2::Gather(controller, localFilenames, 0);
130 
131   // Now write the summary XML on the root node.
132   bool success = true;
133   if (controller == nullptr || controller->GetLocalProcessId() == 0)
134   {
135     success = this->WriteSummaryXML(inputPDS, allFilenames);
136   }
137   if (controller != nullptr && controller->GetNumberOfProcesses() > 1)
138   {
139     int message[2] = { success ? 1 : 0, static_cast<int>(this->GetErrorCode()) };
140     controller->Broadcast(message, 2, 0);
141     success = (message[0] == 1);
142     this->SetErrorCode(message[1]);
143   }
144   else if (success)
145   {
146     this->SetErrorCode(vtkErrorCode::NoError);
147   }
148   this->UpdateProgress(1.0);
149   vtkLogF(TRACE, "success: %d", (int)success);
150   return success ? 1 : 0;
151 }
152 
153 //----------------------------------------------------------------------------
WriteSummaryXML(vtkPartitionedDataSet * input,const std::vector<std::string> & allFilenames)154 bool vtkXMLPartitionedDataSetWriter::WriteSummaryXML(
155   vtkPartitionedDataSet* input, const std::vector<std::string>& allFilenames)
156 {
157   vtkNew<vtkXMLDataWriterHelper> helper;
158   helper->SetWriter(this);
159   helper->SetDataSetVersion(this->GetDataSetMajorVersion(), this->GetDataSetMinorVersion());
160   helper->SetDataSetName(input->GetClassName());
161   if (!helper->OpenFile())
162   {
163     return false;
164   }
165   this->AddArtifact(this->FileName);
166 
167   if (!helper->BeginWriting())
168   {
169     return false;
170   }
171 
172   // build and serialize the DOM.
173   vtkNew<vtkXMLDataElement> root;
174   root->SetName(input->GetClassName());
175   int index = 0;
176   for (auto& fname : allFilenames)
177   {
178     if (!fname.empty()) // fname will be empty for null nodes in the input.
179     {
180       vtkNew<vtkXMLDataElement> child;
181       child->SetName("DataSet");
182       child->SetIntAttribute("index", index);
183       child->SetAttribute("file", fname.c_str());
184       root->AddNestedElement(child);
185     }
186     ++index;
187   }
188 
189   helper->AddXML(root);
190   helper->AddGlobalFieldData(input);
191   if (!helper->EndWriting())
192   {
193     return false;
194   }
195   return true;
196 }
197 
198 //----------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)199 void vtkXMLPartitionedDataSetWriter::PrintSelf(ostream& os, vtkIndent indent)
200 {
201   this->Superclass::PrintSelf(os, indent);
202 }
203