1 /*=========================================================================
2
3 Program: Visualization Toolkit
4 Module: VTXHelper.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
16 /*
17 * VTXHelper.cxx
18 *
19 * Created on: May 3, 2019
20 * Author: William F Godoy godoywf@ornl.gov
21 */
22
23 #include "VTXHelper.h"
24 #include "VTXHelper.txx"
25
26 #include <fstream>
27 #include <numeric> //std::accumulate
28 #include <sstream>
29
30 #include "vtkMPI.h"
31 #include "vtkMPICommunicator.h"
32 #include "vtkMultiProcessController.h"
33
34 #include <vtksys/FStream.hxx>
35 #include <vtksys/SystemTools.hxx>
36
37 namespace vtx
38 {
39 namespace helper
40 {
41
MPIGetComm()42 MPI_Comm MPIGetComm()
43 {
44 MPI_Comm comm = MPI_COMM_NULL;
45 vtkMultiProcessController* controller = vtkMultiProcessController::GetGlobalController();
46 vtkMPICommunicator* vtkComm = vtkMPICommunicator::SafeDownCast(controller->GetCommunicator());
47 if (vtkComm)
48 {
49 if (vtkComm->GetMPIComm())
50 {
51 comm = *(vtkComm->GetMPIComm()->GetHandle());
52 }
53 }
54 return comm;
55 }
56
MPIGetRank()57 int MPIGetRank()
58 {
59 MPI_Comm comm = MPIGetComm();
60 int rank;
61 MPI_Comm_rank(comm, &rank);
62 return rank;
63 }
64
MPIGetSize()65 int MPIGetSize()
66 {
67 MPI_Comm comm = MPIGetComm();
68 int size;
69 MPI_Comm_size(comm, &size);
70 return size;
71 }
72
XMLDocument(const std::string & input,const bool debugMode,const std::string & hint)73 pugi::xml_document XMLDocument(
74 const std::string& input, const bool debugMode, const std::string& hint)
75 {
76 pugi::xml_document document;
77
78 pugi::xml_parse_result result =
79 document.load_buffer(const_cast<char*>(input.data()), input.size());
80
81 if (debugMode)
82 {
83 if (!result)
84 {
85 throw std::invalid_argument(
86 "ERROR: XML: parse error in XML string, description: " + std::string(result.description()) +
87 ", check with any XML editor if format is ill-formed, " + hint + "\n");
88 }
89 }
90 return document;
91 }
92
XMLNode(const std::string nodeName,const pugi::xml_document & xmlDocument,const bool debugMode,const std::string & hint,const bool isMandatory,const bool isUnique)93 pugi::xml_node XMLNode(const std::string nodeName, const pugi::xml_document& xmlDocument,
94 const bool debugMode, const std::string& hint, const bool isMandatory, const bool isUnique)
95 {
96 const pugi::xml_node node = xmlDocument.child(nodeName.c_str());
97
98 if (debugMode)
99 {
100 if (isMandatory && !node)
101 {
102 throw std::invalid_argument("ERROR: XML: no <" + nodeName + "> element found, " + hint);
103 }
104
105 if (isUnique)
106 {
107 const size_t nodes = std::distance(xmlDocument.children(nodeName.c_str()).begin(),
108 xmlDocument.children(nodeName.c_str()).end());
109 if (nodes > 1)
110 {
111 throw std::invalid_argument("ERROR: XML only one <" + nodeName +
112 "> element can exist inside " + std::string(xmlDocument.name()) + ", " + hint + "\n");
113 }
114 }
115 }
116 return node;
117 }
118
XMLNode(const std::string nodeName,const pugi::xml_node & upperNode,const bool debugMode,const std::string & hint,const bool isMandatory,const bool isUnique)119 pugi::xml_node XMLNode(const std::string nodeName, const pugi::xml_node& upperNode,
120 const bool debugMode, const std::string& hint, const bool isMandatory, const bool isUnique)
121 {
122 const pugi::xml_node node = upperNode.child(nodeName.c_str());
123
124 if (debugMode)
125 {
126 if (isMandatory && !node)
127 {
128 throw std::invalid_argument("ERROR: XML: no <" + nodeName + "> element found, inside <" +
129 std::string(upperNode.name()) + "> element " + hint);
130 }
131
132 if (isUnique)
133 {
134 const size_t nodes = std::distance(
135 upperNode.children(nodeName.c_str()).begin(), upperNode.children(nodeName.c_str()).end());
136 if (nodes > 1)
137 {
138 throw std::invalid_argument("ERROR: XML only one <" + nodeName +
139 "> element can exist inside <" + std::string(upperNode.name()) + "> element, " + hint +
140 "\n");
141 }
142 }
143 }
144 return node;
145 }
146
XMLAttribute(const std::string attributeName,const pugi::xml_node & node,const bool debugMode,const std::string & hint,const bool isMandatory)147 pugi::xml_attribute XMLAttribute(const std::string attributeName, const pugi::xml_node& node,
148 const bool debugMode, const std::string& hint, const bool isMandatory)
149 {
150 const pugi::xml_attribute attribute = node.attribute(attributeName.c_str());
151
152 if (debugMode)
153 {
154 if (isMandatory && !attribute)
155 {
156 const std::string nodeName(node.name());
157
158 throw std::invalid_argument("ERROR: XML: No attribute " + attributeName + " found on <" +
159 nodeName + "> element" + hint);
160 }
161 }
162 return attribute;
163 }
164
XMLInitDataSet(const pugi::xml_node & dataSetNode,const std::set<std::string> & specialNames)165 types::DataSet XMLInitDataSet(
166 const pugi::xml_node& dataSetNode, const std::set<std::string>& specialNames)
167 {
168 types::DataSet dataSet;
169
170 for (const pugi::xml_node& dataArrayNode : dataSetNode)
171 {
172 const pugi::xml_attribute xmlName = XMLAttribute(
173 "Name", dataArrayNode, true, "when parsing Name attribute in ADIOS2 VTK XML schema", true);
174 auto result = dataSet.emplace(xmlName.value(), types::DataArray());
175 types::DataArray& dataArray = result.first->second;
176
177 // handle special names
178 const std::string name(xmlName.value());
179 auto itSpecialName = specialNames.find(name);
180 const bool isSpecialName = (itSpecialName != specialNames.end()) ? true : false;
181 if (isSpecialName)
182 {
183 const std::string specialName = *itSpecialName;
184 if (specialName == "connectivity")
185 {
186 dataArray.IsIdType = true;
187 dataArray.Persist = true;
188 }
189 else if (specialName == "vertices")
190 {
191 dataArray.HasTuples = true;
192 dataArray.Persist = true;
193
194 const pugi::xml_attribute xmlOrder = XMLAttribute("Ordering", dataArrayNode, true,
195 "when parsing vertices \"Order\" attribute in ADIOS2 VTK XML schema", false);
196 const std::string order(xmlOrder.value());
197 // XXXX, YYYY, ZZZZ struct of arrays
198 if (order == "SOA")
199 {
200 dataArray.IsSOA = true;
201 }
202 }
203 else if (specialName == "types")
204 {
205 dataArray.Persist = true;
206 }
207 }
208
209 // not mandatory
210 const pugi::xml_attribute xmlNumberOfComponents =
211 XMLAttribute("NumberOfComponents", dataArrayNode, true,
212 "when parsing NumberOfComponents attribute in ADIOS2 "
213 "VTK XML schema",
214 false);
215
216 // TODO enable vector support
217 if (!xmlNumberOfComponents && !isSpecialName)
218 {
219 continue;
220 }
221
222 // these are node_pcdata
223 for (const pugi::xml_node& componentNode : dataArrayNode)
224 {
225 if (componentNode.type() != pugi::node_pcdata)
226 {
227 throw std::runtime_error("ERROR: NumberOfComponents attribute found, but "
228 "component " +
229 std::string(componentNode.name()) + " in node " + std::string(dataArrayNode.value()) +
230 " is not of plain data type in ADIOS2 VTK XML schema\n");
231 }
232 // TRIM
233 std::string variablePCData(componentNode.value());
234 variablePCData.erase(0, variablePCData.find_first_not_of(" \n\r\t"));
235 variablePCData.erase(variablePCData.find_last_not_of(" \n\r\t") + 1);
236
237 dataArray.VectorVariables.push_back(variablePCData);
238 }
239
240 if (xmlNumberOfComponents)
241 {
242 const size_t components = static_cast<size_t>(std::stoull(xmlNumberOfComponents.value()));
243 if (dataArray.VectorVariables.size() != components)
244 {
245 throw std::runtime_error("ERROR: NumberOfComponents " + std::to_string(components) +
246 " and variable names found " + std::to_string(dataArray.VectorVariables.size()) +
247 " inside DataArray node " + std::string(xmlName.name()) + " in ADIOS2 VTK XML schema");
248 }
249 }
250
251 if (dataArray.IsScalar() && (name == "TIME" || name == "CYCLE"))
252 {
253 throw std::invalid_argument("ERROR: data array " + name +
254 " expected to have a least one component, in ADIOS2 VTK XML "
255 "schema\n");
256 }
257 }
258
259 return dataSet;
260 }
261
FileToString(const std::string & fileName)262 std::string FileToString(const std::string& fileName)
263 {
264 vtksys::ifstream file(fileName.c_str());
265 std::stringstream schemaSS;
266 schemaSS << file.rdbuf();
267 return schemaSS.str();
268 }
269
SetToCSV(const std::set<std::string> & input)270 std::string SetToCSV(const std::set<std::string>& input) noexcept
271 {
272 std::string csv = "{ ";
273 for (const std::string& el : input)
274 {
275 csv += el + ", ";
276 }
277 if (!input.empty())
278 {
279 csv.pop_back();
280 csv.pop_back();
281 csv += " }";
282 }
283 return csv;
284 }
285
TotalElements(const std::vector<std::size_t> & dimensions)286 std::size_t TotalElements(const std::vector<std::size_t>& dimensions) noexcept
287 {
288 return std::accumulate(dimensions.begin(), dimensions.end(), 1, std::multiplies<std::size_t>());
289 }
290
291 // allowed types
292 template vtkSmartPointer<vtkDataArray> NewDataArray<int>();
293 template vtkSmartPointer<vtkDataArray> NewDataArray<unsigned int>();
294 template vtkSmartPointer<vtkDataArray> NewDataArray<long int>();
295 template vtkSmartPointer<vtkDataArray> NewDataArray<unsigned long int>();
296 template vtkSmartPointer<vtkDataArray> NewDataArray<long long int>();
297 template vtkSmartPointer<vtkDataArray> NewDataArray<unsigned long long int>();
298 template vtkSmartPointer<vtkDataArray> NewDataArray<float>();
299 template vtkSmartPointer<vtkDataArray> NewDataArray<double>();
300
PartitionCart1D(const adios2::Dims & shape)301 adios2::Box<adios2::Dims> PartitionCart1D(const adios2::Dims& shape)
302 {
303 adios2::Box<adios2::Dims> selection({ adios2::Dims(shape.size(), 0), shape });
304
305 const size_t mpiRank = static_cast<size_t>(MPIGetRank());
306 const size_t mpiSize = static_cast<size_t>(MPIGetSize());
307
308 // slowest index
309 if (shape[0] >= mpiSize)
310 {
311 const size_t elements = shape[0] / mpiSize;
312 // start
313 selection.first[0] = mpiRank * elements;
314 // count
315 selection.second[0] = (mpiRank == mpiSize - 1) ? elements + shape[0] % mpiSize : elements;
316 }
317
318 return selection;
319 }
320
LinearizePoint(const adios2::Dims & shape,const adios2::Dims & point)321 size_t LinearizePoint(const adios2::Dims& shape, const adios2::Dims& point) noexcept
322 {
323 const size_t i = point[0];
324 const size_t j = point[1];
325 const size_t k = point[2];
326
327 const size_t Ny = shape[1];
328 const size_t Nz = shape[2];
329
330 return i * Ny * Nz + j * Nz + k;
331 }
332
NewDataArrayIdType()333 vtkSmartPointer<vtkIdTypeArray> NewDataArrayIdType()
334 {
335 return vtkSmartPointer<vtkIdTypeArray>::New();
336 }
337
GetFileName(const std::string & fileName)338 std::string GetFileName(const std::string& fileName) noexcept
339 {
340 std::string output =
341 EndsWith(fileName, ".bp.dir") ? fileName.substr(0, fileName.size() - 4) : fileName;
342 return output;
343 }
344
GetEngineType(const std::string & fileName)345 std::string GetEngineType(const std::string& fileName) noexcept
346 {
347 std::string engineType = vtksys::SystemTools::FileIsDirectory(fileName) ? "BP4" : "BP3";
348 return engineType;
349 }
350
EndsWith(const std::string & input,const std::string & ends)351 bool EndsWith(const std::string& input, const std::string& ends) noexcept
352 {
353 if (input.length() >= ends.length())
354 {
355 return (!input.compare(input.length() - ends.length(), ends.length(), ends));
356 }
357 return false;
358 }
359
360 } // end helper namespace
361 } // end adios2vtk namespace
362