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