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