Line |
Branch |
Exec |
Source |
1 |
|
|
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
2 |
|
|
// vi: set et ts=4 sw=4 sts=4: |
3 |
|
|
// |
4 |
|
|
// SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder |
5 |
|
|
// SPDX-License-Identifier: GPL-3.0-or-later |
6 |
|
|
// |
7 |
|
|
/*! |
8 |
|
|
* \file |
9 |
|
|
* \ingroup InputOutput |
10 |
|
|
* \brief A vtk file reader using tinyxml2 as xml backend |
11 |
|
|
*/ |
12 |
|
|
#ifndef DUMUX_IO_VTK_VTKREADER_HH |
13 |
|
|
#define DUMUX_IO_VTK_VTKREADER_HH |
14 |
|
|
|
15 |
|
|
#include <iostream> |
16 |
|
|
#include <iterator> |
17 |
|
|
#include <algorithm> |
18 |
|
|
#include <memory> |
19 |
|
|
#include <type_traits> |
20 |
|
|
#include <unordered_map> |
21 |
|
|
#include <utility> |
22 |
|
|
#include <filesystem> |
23 |
|
|
|
24 |
|
|
#include <dune/common/parallel/mpihelper.hh> |
25 |
|
|
#include <dune/common/exceptions.hh> |
26 |
|
|
#include <dune/grid/common/capabilities.hh> |
27 |
|
|
#include <dune/grid/io/file/vtk/common.hh> |
28 |
|
|
#include <dune/grid/common/gridfactory.hh> |
29 |
|
|
|
30 |
|
|
#include <dumux/io/container.hh> |
31 |
|
|
#include <dumux/io/xml/tinyxml2.h> |
32 |
|
|
|
33 |
|
|
namespace Dumux { |
34 |
|
|
|
35 |
|
|
/*! |
36 |
|
|
* \ingroup InputOutput |
37 |
|
|
* \brief A vtk file reader using tinyxml2 as xml backend |
38 |
|
|
*/ |
39 |
|
|
class VTKReader |
40 |
|
|
{ |
41 |
|
|
public: |
42 |
|
|
/*! |
43 |
|
|
* \brief The data array types |
44 |
|
|
*/ |
45 |
|
|
enum class DataType { cellData, pointData }; |
46 |
|
|
|
47 |
|
|
//! the cell / point data type for point data read from a grid file |
48 |
|
|
using Data = std::unordered_map<std::string, std::vector<double>>; |
49 |
|
|
|
50 |
|
|
/*! |
51 |
|
|
* \brief The constructor creates a tinyxml2::XMLDocument from file |
52 |
|
|
*/ |
53 |
|
✗ |
explicit VTKReader(const std::string& fileName) |
54 |
|
✗ |
{ |
55 |
|
✗ |
using namespace tinyxml2; |
56 |
|
✗ |
const auto ext = std::filesystem::path(fileName).extension().string(); |
57 |
|
|
// If in parallel and the file to read is a parallel piece collection (pvtu/pvtp) |
58 |
|
|
// read only the piece belonging to the own process. For this to work, the files |
59 |
|
|
// have to have exactly the same amount of pieces than processes. |
60 |
|
✗ |
fileName_ = Dune::MPIHelper::instance().size() > 1 && ext[1] == 'p' ? |
61 |
|
✗ |
getProcessFileName_(fileName) : fileName; |
62 |
|
|
|
63 |
|
✗ |
const auto eResult = doc_.LoadFile(fileName_.c_str()); |
64 |
|
✗ |
if (eResult != tinyxml2::XML_SUCCESS) |
65 |
|
✗ |
DUNE_THROW(Dune::IOError, "Couldn't open XML file " << fileName_ << "."); |
66 |
|
|
|
67 |
|
✗ |
const XMLElement* pieceNode = getPieceNode_(); |
68 |
|
✗ |
if (pieceNode == nullptr) |
69 |
|
✗ |
DUNE_THROW(Dune::IOError, "Couldn't get 'Piece' node in " << fileName_ << "."); |
70 |
|
✗ |
} |
71 |
|
|
|
72 |
|
|
/*! |
73 |
|
|
* \brief Reviews data from the vtk file to check if there is a data array with a specified name |
74 |
|
|
* \param name the name attribute of the data array to read |
75 |
|
|
* \param type the data array type |
76 |
|
|
*/ |
77 |
|
✗ |
bool hasData(const std::string& name, const DataType& type) const |
78 |
|
|
{ |
79 |
|
✗ |
using namespace tinyxml2; |
80 |
|
|
|
81 |
|
✗ |
const XMLElement* pieceNode = getPieceNode_(); |
82 |
|
✗ |
const XMLElement* dataNode = getDataNode_(pieceNode, type); |
83 |
|
✗ |
if (dataNode == nullptr) |
84 |
|
|
return false; |
85 |
|
|
|
86 |
|
✗ |
const XMLElement* dataArray = findDataArray_(dataNode, name); |
87 |
|
✗ |
if (dataArray == nullptr) |
88 |
|
✗ |
return false; |
89 |
|
|
|
90 |
|
|
return true; |
91 |
|
|
} |
92 |
|
|
|
93 |
|
|
/*! |
94 |
|
|
* \brief read data from the vtk file to a container, e.g. std::vector<double> |
95 |
|
|
* \tparam Container a container type that has begin(), end(), push_back(), e.g. std::vector<> |
96 |
|
|
* \param name the name attribute of the data array to read |
97 |
|
|
* \param type the data array type |
98 |
|
|
*/ |
99 |
|
|
template<class Container> |
100 |
|
✗ |
Container readData(const std::string& name, const DataType& type) const |
101 |
|
|
{ |
102 |
|
|
using namespace tinyxml2; |
103 |
|
|
|
104 |
|
✗ |
const XMLElement* pieceNode = getPieceNode_(); |
105 |
|
✗ |
const XMLElement* dataNode = getDataNode_(pieceNode, type); |
106 |
|
✗ |
if (dataNode == nullptr) |
107 |
|
✗ |
DUNE_THROW(Dune::IOError, "Couldn't get 'PointData' or 'CellData' node in " << fileName_ << "."); |
108 |
|
|
|
109 |
|
✗ |
const XMLElement* dataArray = findDataArray_(dataNode, name); |
110 |
|
✗ |
if (dataArray == nullptr) |
111 |
|
✗ |
DUNE_THROW(Dune::IOError, "Couldn't find the data array " << name << "."); |
112 |
|
|
|
113 |
|
✗ |
return parseDataArray_<Container>(dataArray); |
114 |
|
|
} |
115 |
|
|
|
116 |
|
|
/*! |
117 |
|
|
* \brief Read a grid from a vtk/vtu/vtp file, ignoring cell and point data |
118 |
|
|
* \param verbose if the output should be verbose |
119 |
|
|
*/ |
120 |
|
|
template<class Grid> |
121 |
|
|
std::unique_ptr<Grid> readGrid(bool verbose = false) const |
122 |
|
|
{ |
123 |
|
|
static_assert(!Dune::Capabilities::isCartesian<Grid>::v, "Grid reader only supports unstructured grid implementations"); |
124 |
|
|
|
125 |
|
|
// make a grid factory |
126 |
|
|
Dune::GridFactory<Grid> factory; |
127 |
|
|
|
128 |
|
|
// only read on rank 0 |
129 |
|
|
if (Dune::MPIHelper::instance().rank() == 0) |
130 |
|
|
{ |
131 |
|
|
if (verbose) |
132 |
|
|
std::cout << "Reading " << Grid::dimension << "d grid from vtk file " << fileName_ << "." << std::endl; |
133 |
|
|
readGrid_(factory, verbose); |
134 |
|
|
} |
135 |
|
|
|
136 |
|
|
return std::unique_ptr<Grid>(factory.createGrid()); |
137 |
|
|
} |
138 |
|
|
|
139 |
|
|
/*! |
140 |
|
|
* \brief Read a grid from a vtk/vtu/vtp file, ignoring cell and point data |
141 |
|
|
* \note use this signature if the factory might be needed outside to interpret the data via the factory's insertion indices |
142 |
|
|
* \param verbose if the output should be verbose |
143 |
|
|
* \param factory the (empty) grid factory |
144 |
|
|
*/ |
145 |
|
|
template<class Grid> |
146 |
|
|
std::unique_ptr<Grid> readGrid(Dune::GridFactory<Grid>& factory, bool verbose = false) const |
147 |
|
|
{ |
148 |
|
|
static_assert(!Dune::Capabilities::isCartesian<Grid>::v, "Grid reader only supports unstructured grid implementations"); |
149 |
|
|
|
150 |
|
|
if (Dune::MPIHelper::instance().rank() == 0) |
151 |
|
|
{ |
152 |
|
|
if (verbose) |
153 |
|
|
std::cout << "Reading " << Grid::dimension << "d grid from vtk file " << fileName_ << "." << std::endl; |
154 |
|
|
readGrid_(factory, verbose); |
155 |
|
|
} |
156 |
|
|
|
157 |
|
|
return std::unique_ptr<Grid>(factory.createGrid()); |
158 |
|
|
} |
159 |
|
|
|
160 |
|
|
/*! |
161 |
|
|
* \brief Read a grid from a vtk/vtu/vtp file, reading all cell and point data |
162 |
|
|
* \note the factory will be needed outside to interpret the data via the factory's insertion indices |
163 |
|
|
* \param factory the (empty) grid factory |
164 |
|
|
* \param cellData the cell data arrays to be filled |
165 |
|
|
* \param pointData the point data arrays to be filled |
166 |
|
|
* \param verbose if the output should be verbose |
167 |
|
|
*/ |
168 |
|
|
template<class Grid> |
169 |
|
✗ |
std::unique_ptr<Grid> readGrid(Dune::GridFactory<Grid>& factory, Data& cellData, Data& pointData, bool verbose = false) const |
170 |
|
|
{ |
171 |
|
|
static_assert(!Dune::Capabilities::isCartesian<Grid>::v, "Grid reader only supports unstructured grid implementations"); |
172 |
|
|
|
173 |
|
✗ |
if (Dune::MPIHelper::instance().rank() == 0) |
174 |
|
|
{ |
175 |
|
✗ |
if (verbose) |
176 |
|
✗ |
std::cout << "Reading " << Grid::dimension << "d grid from vtk file " << fileName_ << "." << std::endl; |
177 |
|
✗ |
readGrid_(factory, verbose); |
178 |
|
✗ |
readGridData_(cellData, pointData, verbose); |
179 |
|
|
} |
180 |
|
|
|
181 |
|
✗ |
return std::unique_ptr<Grid>(factory.createGrid()); |
182 |
|
|
} |
183 |
|
|
|
184 |
|
|
private: |
185 |
|
|
/*! |
186 |
|
|
* \brief get the vtk filename for the current processor |
187 |
|
|
*/ |
188 |
|
✗ |
std::string getProcessFileName_(const std::string& pvtkFileName) |
189 |
|
|
{ |
190 |
|
✗ |
using namespace tinyxml2; |
191 |
|
|
|
192 |
|
✗ |
XMLDocument pDoc; |
193 |
|
✗ |
const auto eResult = pDoc.LoadFile(pvtkFileName.c_str()); |
194 |
|
✗ |
if (eResult != XML_SUCCESS) |
195 |
|
✗ |
DUNE_THROW(Dune::IOError, "Couldn't open XML file " << pvtkFileName << "."); |
196 |
|
|
|
197 |
|
|
// get the first piece node |
198 |
|
✗ |
const XMLElement* pieceNode = getPieceNode_(pDoc, pvtkFileName); |
199 |
|
✗ |
const auto myrank = Dune::MPIHelper::instance().rank(); |
200 |
|
✗ |
for (int rank = 0; rank < myrank; ++rank) |
201 |
|
|
{ |
202 |
|
✗ |
pieceNode = pieceNode->NextSiblingElement("Piece"); |
203 |
|
✗ |
if (pieceNode == nullptr) |
204 |
|
✗ |
DUNE_THROW(Dune::IOError, "Couldn't find 'Piece' node for rank " |
205 |
|
|
<< rank << " in " << pvtkFileName << "."); |
206 |
|
|
} |
207 |
|
|
|
208 |
|
✗ |
const char *vtkFileName = pieceNode->Attribute("Source"); |
209 |
|
✗ |
if (vtkFileName == nullptr) |
210 |
|
✗ |
DUNE_THROW(Dune::IOError, "Couldn't get 'Source' attribute of 'Piece' node no. " << myrank << " in " << pvtkFileName); |
211 |
|
|
|
212 |
|
✗ |
return vtkFileName; |
213 |
|
|
} |
214 |
|
|
|
215 |
|
|
/*! |
216 |
|
|
* \brief Read a grid from a vtk/vtu/vtp file |
217 |
|
|
* \param factory the (empty) grid factory |
218 |
|
|
* \param verbose if the output should be verbose |
219 |
|
|
*/ |
220 |
|
|
template<class Grid> |
221 |
|
✗ |
void readGrid_(Dune::GridFactory<Grid>& factory, bool verbose = false) const |
222 |
|
|
{ |
223 |
|
|
using namespace tinyxml2; |
224 |
|
|
|
225 |
|
✗ |
const XMLElement* pieceNode = getPieceNode_(); |
226 |
|
✗ |
const XMLElement* pointsNode = pieceNode->FirstChildElement("Points")->FirstChildElement("DataArray"); |
227 |
|
✗ |
if (pointsNode == nullptr) |
228 |
|
✗ |
DUNE_THROW(Dune::IOError, "Couldn't get data array of points in " << fileName_ << "."); |
229 |
|
|
|
230 |
|
|
using Point3D = Dune::FieldVector<double, 3>; |
231 |
|
✗ |
std::vector<Point3D> points3D; |
232 |
|
✗ |
std::stringstream dataStream(pointsNode->GetText()); |
233 |
|
✗ |
std::istream_iterator<Point3D> it(dataStream); |
234 |
|
✗ |
std::copy(it, std::istream_iterator<Point3D>(), std::back_inserter(points3D)); |
235 |
|
|
|
236 |
|
|
// adapt point dimensions if grid dimension is smaller than 3 |
237 |
|
✗ |
auto points = adaptPointDimension_<Grid::dimensionworld>(std::move(points3D)); |
238 |
|
|
|
239 |
|
✗ |
if (verbose) std::cout << "Found " << points.size() << " vertices." << std::endl; |
240 |
|
|
|
241 |
|
|
// insert vertices to the grid factory |
242 |
|
✗ |
for (auto&& point : points) |
243 |
|
✗ |
factory.insertVertex(std::move(point)); |
244 |
|
|
|
245 |
|
✗ |
const XMLElement* cellsNode = pieceNode->FirstChildElement("Cells"); |
246 |
|
✗ |
const XMLElement* linesNode = pieceNode->FirstChildElement("Lines"); |
247 |
|
✗ |
if (cellsNode) |
248 |
|
|
{ |
249 |
|
✗ |
const XMLElement* connectivityNode = findDataArray_(cellsNode, "connectivity"); |
250 |
|
✗ |
const XMLElement* offsetsNode = findDataArray_(cellsNode, "offsets"); |
251 |
|
✗ |
const XMLElement* typesNode = findDataArray_(cellsNode, "types"); |
252 |
|
|
|
253 |
|
✗ |
const auto connectivity = parseDataArray_<std::vector<unsigned int>>(connectivityNode); |
254 |
|
✗ |
const auto offsets = parseDataArray_<std::vector<unsigned int>>(offsetsNode); |
255 |
|
✗ |
const auto types = parseDataArray_<std::vector<unsigned int>>(typesNode); |
256 |
|
|
|
257 |
|
✗ |
if (verbose) std::cout << "Found " << offsets.size() << " elements." << std::endl; |
258 |
|
|
|
259 |
|
|
unsigned int lastOffset = 0; |
260 |
|
✗ |
for (unsigned int i = 0; i < offsets.size(); ++i) |
261 |
|
|
{ |
262 |
|
✗ |
const auto geomType = vtkToDuneGeomType_(types[i]); |
263 |
|
✗ |
unsigned int offset = offsets[i]; |
264 |
|
✗ |
std::vector<unsigned int> corners; corners.resize(offset-lastOffset); |
265 |
|
✗ |
for (unsigned int j = 0; j < offset-lastOffset; ++j) |
266 |
|
✗ |
corners[Dune::VTK::renumber(geomType, j)] = connectivity[lastOffset+j]; |
267 |
|
✗ |
factory.insertElement(geomType, std::move(corners)); |
268 |
|
✗ |
lastOffset = offset; |
269 |
|
|
} |
270 |
|
|
} |
271 |
|
|
// for poly data |
272 |
|
✗ |
else if (linesNode) |
273 |
|
|
{ |
274 |
|
|
// sanity check |
275 |
|
|
if (Grid::dimension != 1) |
276 |
|
✗ |
DUNE_THROW(Dune::IOError, "Grid expects dimension " << Grid::dimension |
277 |
|
|
<< " but " << fileName_ << " contains a 1D grid."); |
278 |
|
|
|
279 |
|
✗ |
const XMLElement* connectivityNode = findDataArray_(linesNode, "connectivity"); |
280 |
|
✗ |
const XMLElement* offsetsNode = findDataArray_(linesNode, "offsets"); |
281 |
|
|
|
282 |
|
✗ |
const auto connectivity = parseDataArray_<std::vector<unsigned int>>(connectivityNode); |
283 |
|
✗ |
const auto offsets = parseDataArray_<std::vector<unsigned int>>(offsetsNode); |
284 |
|
|
|
285 |
|
✗ |
if (verbose) std::cout << "Found " << offsets.size() << " polylines." << std::endl; |
286 |
|
|
|
287 |
|
|
unsigned int lastOffset = 0; |
288 |
|
✗ |
for (unsigned int i = 0; i < offsets.size(); ++i) |
289 |
|
|
{ |
290 |
|
|
// a polyline can have many points in the VTK format |
291 |
|
|
// split the line in segments with two points |
292 |
|
✗ |
unsigned int offset = offsets[i]; |
293 |
|
✗ |
for (unsigned int j = 0; j < offset-lastOffset-1; ++j) |
294 |
|
✗ |
factory.insertElement(Dune::GeometryTypes::line, |
295 |
|
✗ |
std::vector<unsigned int>({ connectivity[lastOffset+j], connectivity[lastOffset+j+1] })); |
296 |
|
✗ |
lastOffset = offset; |
297 |
|
|
} |
298 |
|
|
} |
299 |
|
|
else |
300 |
|
✗ |
DUNE_THROW(Dune::IOError, "No Cells or Lines element found in " << fileName_); |
301 |
|
✗ |
} |
302 |
|
|
|
303 |
|
|
/*! |
304 |
|
|
* \brief Read a grid data from a vtk/vtu/vtp file |
305 |
|
|
* \param cellData the cell data arrays to be filled |
306 |
|
|
* \param pointData the point data arrays to be filled |
307 |
|
|
* \param verbose if the output should be verbose |
308 |
|
|
*/ |
309 |
|
✗ |
void readGridData_(Data& cellData, Data& pointData, bool verbose = false) const |
310 |
|
|
{ |
311 |
|
✗ |
using namespace tinyxml2; |
312 |
|
|
|
313 |
|
✗ |
const XMLElement* pieceNode = getPieceNode_(); |
314 |
|
✗ |
const XMLElement* cellDataNode = getDataNode_(pieceNode, DataType::cellData); |
315 |
|
✗ |
if (cellDataNode != nullptr) |
316 |
|
|
{ |
317 |
|
✗ |
const XMLElement* cellsNode = pieceNode->FirstChildElement("Cells"); |
318 |
|
✗ |
const XMLElement* linesNode = pieceNode->FirstChildElement("Lines"); |
319 |
|
|
|
320 |
|
✗ |
if (cellsNode) |
321 |
|
|
{ |
322 |
|
✗ |
const XMLElement* dataArray = cellDataNode->FirstChildElement("DataArray"); |
323 |
|
✗ |
for (; dataArray != nullptr; dataArray = dataArray->NextSiblingElement("DataArray")) |
324 |
|
|
{ |
325 |
|
✗ |
const char *attributeText = dataArray->Attribute("Name"); |
326 |
|
|
|
327 |
|
✗ |
if (attributeText == nullptr) |
328 |
|
✗ |
DUNE_THROW(Dune::IOError, "Couldn't get Name attribute of a cell data array."); |
329 |
|
|
|
330 |
|
✗ |
cellData[std::string(attributeText)] = parseDataArray_<std::vector<double>>(dataArray); |
331 |
|
|
|
332 |
|
✗ |
if (verbose) |
333 |
|
✗ |
std::cout << "Read cell data field " << attributeText << std::endl; |
334 |
|
|
} |
335 |
|
|
} |
336 |
|
|
// for poly data |
337 |
|
✗ |
else if (linesNode) |
338 |
|
|
{ |
339 |
|
|
// first parse all the cell data (each cell in this sense can be a polyline) |
340 |
|
✗ |
const XMLElement* dataArray = cellDataNode->FirstChildElement("DataArray"); |
341 |
|
✗ |
if (dataArray) |
342 |
|
|
{ |
343 |
|
✗ |
Data polyLineCellData; |
344 |
|
✗ |
for (; dataArray != nullptr; dataArray = dataArray->NextSiblingElement("DataArray")) |
345 |
|
|
{ |
346 |
|
✗ |
const char *attributeText = dataArray->Attribute("Name"); |
347 |
|
|
|
348 |
|
✗ |
if (attributeText == nullptr) |
349 |
|
✗ |
DUNE_THROW(Dune::IOError, "Couldn't get Name attribute of a cell data array."); |
350 |
|
|
|
351 |
|
✗ |
polyLineCellData[std::string(attributeText)] = parseDataArray_<std::vector<double>>(dataArray); |
352 |
|
|
|
353 |
|
✗ |
if (verbose) |
354 |
|
✗ |
std::cout << "Read cell data field " << attributeText << std::endl; |
355 |
|
|
} |
356 |
|
|
|
357 |
|
|
// a polyline can have many points in the VTK format |
358 |
|
|
// we split the line in segments with two points |
359 |
|
|
// so we also need to duplicate the cell data to fit the increased line number |
360 |
|
✗ |
const XMLElement* offsetsNode = findDataArray_(linesNode, "offsets"); |
361 |
|
✗ |
const auto offsets = parseDataArray_<std::vector<unsigned int>>(offsetsNode); |
362 |
|
|
|
363 |
|
✗ |
if (offsets.size() != polyLineCellData.begin()->second.size()) |
364 |
|
✗ |
DUNE_THROW(Dune::IOError, "Expected the same number of cell data entries (is " |
365 |
|
|
<< polyLineCellData.begin()->second.size() |
366 |
|
|
<< ") as polylines (" << offsets.size() << ")!"); |
367 |
|
|
|
368 |
|
|
// count the number of Dune cells to be able to resize the data vectors |
369 |
|
|
unsigned int lastOffset = 0; |
370 |
|
|
std::size_t numCells = 0; |
371 |
|
✗ |
for (unsigned int i = 0; i < offsets.size(); ++i) |
372 |
|
|
{ |
373 |
|
✗ |
unsigned int offset = offsets[i]; |
374 |
|
✗ |
for (unsigned int j = 0; j < offset-lastOffset-1; ++j) |
375 |
|
✗ |
++numCells; |
376 |
|
✗ |
lastOffset = offset; |
377 |
|
|
} |
378 |
|
|
|
379 |
|
|
// create the data arrays |
380 |
|
✗ |
for (const auto& dArray : polyLineCellData) |
381 |
|
|
{ |
382 |
|
✗ |
cellData[dArray.first] = std::vector<double>(numCells); |
383 |
|
✗ |
auto& cd = cellData[dArray.first]; |
384 |
|
|
const auto& pd = dArray.second; |
385 |
|
|
|
386 |
|
|
lastOffset = 0; |
387 |
|
|
std::size_t cellIdx = 0; |
388 |
|
✗ |
for (unsigned int i = 0; i < offsets.size(); ++i) |
389 |
|
|
{ |
390 |
|
✗ |
unsigned int offset = offsets[i]; |
391 |
|
✗ |
for (unsigned int j = 0; j < offset-lastOffset-1; ++j) |
392 |
|
✗ |
cd[cellIdx++] = pd[i]; |
393 |
|
✗ |
lastOffset = offset; |
394 |
|
|
} |
395 |
|
|
} |
396 |
|
|
} |
397 |
|
|
} |
398 |
|
|
else |
399 |
|
✗ |
DUNE_THROW(Dune::IOError, "No Cells or Lines element found in " << fileName_); |
400 |
|
|
} |
401 |
|
|
|
402 |
|
✗ |
const XMLElement* pointDataNode = getDataNode_(pieceNode, DataType::pointData); |
403 |
|
✗ |
if (pointDataNode != nullptr) |
404 |
|
|
{ |
405 |
|
✗ |
const XMLElement* dataArray = pointDataNode->FirstChildElement("DataArray"); |
406 |
|
✗ |
for (; dataArray != nullptr; dataArray = dataArray->NextSiblingElement("DataArray")) |
407 |
|
|
{ |
408 |
|
✗ |
const char *attributeText = dataArray->Attribute("Name"); |
409 |
|
|
|
410 |
|
✗ |
if (attributeText == nullptr) |
411 |
|
✗ |
DUNE_THROW(Dune::IOError, "Couldn't get Name attribute of a point data array."); |
412 |
|
|
|
413 |
|
✗ |
pointData[std::string(attributeText)] = parseDataArray_<std::vector<double>>(dataArray); |
414 |
|
|
|
415 |
|
✗ |
if (verbose) |
416 |
|
✗ |
std::cout << "Read point data field " << attributeText << std::endl; |
417 |
|
|
} |
418 |
|
|
} |
419 |
|
✗ |
} |
420 |
|
|
|
421 |
|
|
/*! |
422 |
|
|
* \brief Get the piece node of the XMLDocument |
423 |
|
|
* \note Returns nullptr if the piece node wasn't found |
424 |
|
|
*/ |
425 |
|
|
const tinyxml2::XMLElement* getPieceNode_() const |
426 |
|
✗ |
{ return getPieceNode_(doc_, fileName_); } |
427 |
|
|
|
428 |
|
|
/*! |
429 |
|
|
* \brief Get the piece node an xml document |
430 |
|
|
* \note Returns nullptr if the piece node wasn't found |
431 |
|
|
* \param doc an xml document |
432 |
|
|
* \param fileName a file name the doc was created from |
433 |
|
|
*/ |
434 |
|
✗ |
const tinyxml2::XMLElement* getPieceNode_(const tinyxml2::XMLDocument& doc, const std::string& fileName) const |
435 |
|
|
{ |
436 |
|
✗ |
using namespace tinyxml2; |
437 |
|
|
|
438 |
|
✗ |
const XMLElement* pieceNode = doc.FirstChildElement("VTKFile"); |
439 |
|
✗ |
if (pieceNode == nullptr) |
440 |
|
✗ |
DUNE_THROW(Dune::IOError, "Couldn't get 'VTKFile' node in " << fileName << "."); |
441 |
|
|
|
442 |
|
✗ |
pieceNode = pieceNode->FirstChildElement("UnstructuredGrid"); |
443 |
|
✗ |
if (pieceNode == nullptr) |
444 |
|
✗ |
pieceNode = doc.FirstChildElement("VTKFile")->FirstChildElement("PolyData"); |
445 |
|
✗ |
if (pieceNode == nullptr) |
446 |
|
✗ |
pieceNode = doc.FirstChildElement("VTKFile")->FirstChildElement("PUnstructuredGrid"); |
447 |
|
✗ |
if (pieceNode == nullptr) |
448 |
|
✗ |
pieceNode = doc.FirstChildElement("VTKFile")->FirstChildElement("PPolyData"); |
449 |
|
✗ |
if (pieceNode == nullptr) |
450 |
|
✗ |
DUNE_THROW(Dune::IOError, "Couldn't get 'UnstructuredGrid', 'PUnstructuredGrid', 'PolyData', or 'PPolyData' node in " << fileName << "."); |
451 |
|
|
|
452 |
|
✗ |
return pieceNode->FirstChildElement("Piece"); |
453 |
|
|
} |
454 |
|
|
|
455 |
|
|
/*! |
456 |
|
|
* \brief Get the piece node of the XMLDocument |
457 |
|
|
* \param pieceNode the pieceNode of the vtk file |
458 |
|
|
* \param type the vtk data type (cell data or point data) |
459 |
|
|
* \note Returns nullptr if the data node wasn't found |
460 |
|
|
*/ |
461 |
|
✗ |
const tinyxml2::XMLElement* getDataNode_(const tinyxml2::XMLElement* pieceNode, const DataType& type) const |
462 |
|
|
{ |
463 |
|
✗ |
using namespace tinyxml2; |
464 |
|
|
|
465 |
|
✗ |
const XMLElement* dataNode = nullptr; |
466 |
|
✗ |
if (type == DataType::pointData) |
467 |
|
✗ |
dataNode = pieceNode->FirstChildElement("PointData"); |
468 |
|
✗ |
else if (type == DataType::cellData) |
469 |
|
✗ |
dataNode = pieceNode->FirstChildElement("CellData"); |
470 |
|
|
else |
471 |
|
✗ |
DUNE_THROW(Dune::IOError, "Only cell and point data are supported."); |
472 |
|
|
|
473 |
|
✗ |
return dataNode; |
474 |
|
|
} |
475 |
|
|
|
476 |
|
|
/*! |
477 |
|
|
* \brief Find a data array with a specific name |
478 |
|
|
* \param dataNode a cell or point data node |
479 |
|
|
* \param name the name of the data array to be found |
480 |
|
|
* \note Returns nullptr if the data array wasn't found |
481 |
|
|
*/ |
482 |
|
✗ |
const tinyxml2::XMLElement* findDataArray_(const tinyxml2::XMLElement* dataNode, const std::string& name) const |
483 |
|
|
{ |
484 |
|
✗ |
using namespace tinyxml2; |
485 |
|
|
|
486 |
|
|
// loop over XML node siblings to find the correct data array |
487 |
|
✗ |
const XMLElement* dataArray = dataNode->FirstChildElement("DataArray"); |
488 |
|
✗ |
for (; dataArray != nullptr; dataArray = dataArray->NextSiblingElement("DataArray")) |
489 |
|
|
{ |
490 |
|
✗ |
const char *attributeText = dataArray->Attribute("Name"); |
491 |
|
|
|
492 |
|
✗ |
if (attributeText == nullptr) |
493 |
|
✗ |
DUNE_THROW(Dune::IOError, "Couldn't get Name attribute of a data array."); |
494 |
|
|
|
495 |
|
✗ |
if (attributeText == name) |
496 |
|
|
break; |
497 |
|
|
} |
498 |
|
|
|
499 |
|
✗ |
return dataArray; |
500 |
|
|
} |
501 |
|
|
|
502 |
|
|
/*! |
503 |
|
|
* \brief Parses the text of a data array into a container |
504 |
|
|
* \tparam Container a container type that has begin(), end(), push_back(), e.g. std::vector<double> |
505 |
|
|
* \param dataArray the data array node to be parsed |
506 |
|
|
*/ |
507 |
|
|
template<class Container> |
508 |
|
✗ |
Container parseDataArray_(const tinyxml2::XMLElement* dataArray) const |
509 |
|
|
{ |
510 |
|
✗ |
std::stringstream dataStream(dataArray->GetText()); |
511 |
|
✗ |
return readStreamToContainer<Container>(dataStream); |
512 |
|
|
} |
513 |
|
|
|
514 |
|
|
/*! |
515 |
|
|
* \brief Return the Dune::GeometryType for a given VTK geometry type |
516 |
|
|
* \param vtkCellType the vtk cell type |
517 |
|
|
*/ |
518 |
|
✗ |
Dune::GeometryType vtkToDuneGeomType_(unsigned int vtkCellType) const |
519 |
|
|
{ |
520 |
|
✗ |
switch (vtkCellType) |
521 |
|
|
{ |
522 |
|
✗ |
case Dune::VTK::GeometryType::vertex: return Dune::GeometryTypes::vertex; |
523 |
|
✗ |
case Dune::VTK::GeometryType::line: return Dune::GeometryTypes::line; |
524 |
|
✗ |
case Dune::VTK::GeometryType::triangle: return Dune::GeometryTypes::triangle; |
525 |
|
✗ |
case Dune::VTK::GeometryType::quadrilateral: return Dune::GeometryTypes::quadrilateral; |
526 |
|
✗ |
case Dune::VTK::GeometryType::tetrahedron: return Dune::GeometryTypes::tetrahedron; |
527 |
|
✗ |
case Dune::VTK::GeometryType::hexahedron: return Dune::GeometryTypes::hexahedron; |
528 |
|
✗ |
case Dune::VTK::GeometryType::prism: return Dune::GeometryTypes::prism; |
529 |
|
✗ |
case Dune::VTK::GeometryType::pyramid: return Dune::GeometryTypes::pyramid; |
530 |
|
✗ |
default: DUNE_THROW(Dune::NotImplemented, "VTK cell type " << vtkCellType); |
531 |
|
|
} |
532 |
|
|
} |
533 |
|
|
|
534 |
|
|
template<int dim, std::enable_if_t<(dim < 3), int> = 0> |
535 |
|
|
std::vector<Dune::FieldVector<double, dim>> |
536 |
|
✗ |
adaptPointDimension_(std::vector<Dune::FieldVector<double, 3>>&& points3D) const |
537 |
|
|
{ |
538 |
|
✗ |
std::vector<Dune::FieldVector<double, dim>> points(points3D.size()); |
539 |
|
✗ |
for (std::size_t i = 0; i < points.size(); ++i) |
540 |
|
✗ |
for (int j = 0; j < dim; ++j) |
541 |
|
✗ |
points[i][j] = points3D[i][j]; |
542 |
|
✗ |
return points; |
543 |
|
|
} |
544 |
|
|
|
545 |
|
|
template<int dim, std::enable_if_t<(dim == 3), int> = 0> |
546 |
|
|
std::vector<Dune::FieldVector<double, dim>> |
547 |
|
✗ |
adaptPointDimension_(std::vector<Dune::FieldVector<double, 3>>&& points3D) const |
548 |
|
✗ |
{ return std::move(points3D); } |
549 |
|
|
|
550 |
|
|
std::string fileName_; //!< the vtk file name |
551 |
|
|
tinyxml2::XMLDocument doc_; //!< the xml document created from file with name fileName_ |
552 |
|
|
}; |
553 |
|
|
|
554 |
|
|
} // end namespace Dumux |
555 |
|
|
|
556 |
|
|
#endif |
557 |
|
|
|