1 #pragma once
2 
3 #include <iosfwd>
4 #include <map>
5 #include <memory>
6 #include <vector>
7 
8 #include <dune/common/shared_ptr.hh>
9 #include <dune/common/typelist.hh>
10 #include <dune/common/typeutilities.hh>
11 
12 #include <dune/vtk/filereader.hh>
13 #include <dune/vtk/types.hh>
14 #include <dune/vtk/utility/errors.hh>
15 
16 // default GridCreator
17 #include <dune/vtk/gridcreators/continuousgridcreator.hh>
18 #include <dune/vtk/gridfunctions/common.hh>
19 
20 namespace Dune
21 {
22   /// File-Reader for Vtk unstructured .vtu files
23   /**
24    * Reads .vtu files and constructs a grid from the cells stored in the file
25    * Additionally, stored data can be read.
26    *
27    * NOTE: Assumption on the file structure: Each XML tag must be on a separate line.
28    *
29    * \tparam Grid       The type of the grid to construct.
30    * \tparam GC         GridCreator policy type to control what to pass to a grid factory with
31    *                    data given from the file. [ContinuousGridCreator]
32    * \tparam FieldType  Type of the components of the data to extract from the file [default: double]
33    **/
34   template <class Grid, class GC = Vtk::ContinuousGridCreator<Grid>, class FieldType = double>
35   class VtkReader
36       : public Vtk::FileReader<Grid, VtkReader<Grid, GC>>
37   {
38     // Sections visited during the xml parsing
39     enum Sections {
40       NO_SECTION = 0, VTK_FILE, UNSTRUCTURED_GRID, PIECE, POINT_DATA, PD_DATA_ARRAY, CELL_DATA, CD_DATA_ARRAY,
41       POINTS, POINTS_DATA_ARRAY, CELLS, CELLS_DATA_ARRAY, APPENDED_DATA, XML_NAME, XML_NAME_ASSIGN, XML_VALUE
42     };
43 
44     // Type storing information about read data
45     struct DataArrayAttributes
46     {
47       std::string name;
48       Vtk::DataTypes type;
49       unsigned int components = 1;
50       std::uint64_t offset = 0;
51       Sections section = NO_SECTION;
52     };
53 
54     // Type of global world coordinates
55     using GlobalCoordinate = typename GC::GlobalCoordinate;
56 
57     // Template representing a grid-function that is created in getPointData() and getCellData()
58     // with Context either Vtk::PointContext or Vek::CellContext, respectively.
59     // To each GridCreator a GridFunction is associated, see, e.g. Vtk::ContinuousGridFunction
60     // or Vtk::LagrangeGridFunction.
61     template <class Context>
62     using GridFunction = typename Vtk::AssociatedGridFunction<GC, FieldType, Context>::type;
63 
64   public:
65     using GridCreator = GC;
66 
67     /// GridFunction representing the data stored on the points in the file
68     using PointGridFunction = GridFunction<Vtk::PointContext>;
69 
70     /// GridFunction representing the data stored on the cells in the file
71     using CellGridFunction = GridFunction<Vtk::CellContext>;
72 
73   public:
74     /// Constructor. Creates a new GridCreator with the passed factory
75     /**
76      * \param args... Either pass a GridFactory by reference or shared_ptr, or a list of arguments
77      *                passed to the constructor of a Dune::GridFactory (typically and empty parameter
78      *                list). See the constructor of \ref GridCreatorInterface and the GridCreator
79      *                passed to this reader.
80      **/
81     template <class... Args,
82       std::enable_if_t<std::is_constructible<GridCreator, Args...>::value,int> = 0>
VtkReader(Args &&...args)83     explicit VtkReader (Args&&... args)
84       : VtkReader(std::make_shared<GridCreator>(std::forward<Args>(args)...))
85     {}
86 
87     /// Constructor. Stores the references in a non-destroying shared_ptr
VtkReader(GridCreator & creator)88     explicit VtkReader (GridCreator& creator)
89       : VtkReader(stackobject_to_shared_ptr(creator))
90     {}
91 
92     /// Constructor. Stores the shared_ptr
VtkReader(std::shared_ptr<GridCreator> creator)93     explicit VtkReader (std::shared_ptr<GridCreator> creator)
94       : creator_(std::move(creator))
95     {}
96 
97     /// Read the grid from file with `filename` into the GridCreator
98     /**
99      * This function fills internal data containers representing the information from the
100      * passed file.
101      *
102      * \param filename     The name of the input file
103      * \param fillCreator  If `false`, only fill internal data structures, if `true`, pass
104      *                     the internal data to the GridCreator. [true]
105      **/
106     void read (std::string const& filename, bool fillCreator = true);
107 
108     /// Obtains the creator of the reader
gridCreator()109     GridCreator& gridCreator ()
110     {
111       return *creator_;
112     }
113 
114     /// Construct the actual grid using the GridCreator
115     /// [[expects: read_ == true]]
createGrid() const116     std::unique_ptr<Grid> createGrid () const
117     {
118       return creator_->createGrid();
119     }
120 
121     /// Construct a grid-function representing the point-data with the given name
122     /// [[expects: read_ == true]]
getPointData(std::string const & name) const123     GridFunction<Vtk::PointContext> getPointData (std::string const& name) const
124     {
125       auto data_it = dataArray_.find("PointData." + name);
126       auto point_it = pointData_.find("PointData." + name);
127       VTK_ASSERT_MSG(data_it != dataArray_.end() && point_it != pointData_.end(),
128         "The data to extract is not found in point-data. Try `getCellData()` instead!");
129       VTK_ASSERT(data_it->second.section == POINT_DATA);
130 
131       return {*creator_, point_it->second, data_it->second.name, data_it->second.components,
132               data_it->second.type, vec_types, vec_offsets, vec_connectivity};
133     }
134 
135     /// Return a vector of DataArrayAttributes for all POINT_DATA blocks
136     /// [[expects: read_ == true]]
getPointDataAttributes() const137     std::vector<DataArrayAttributes> getPointDataAttributes () const
138     {
139       std::vector<DataArrayAttributes> attributes;
140       attributes.reserve(pointData_.size());
141       for (auto const& da : dataArray_) {
142         if (da.second.section == POINT_DATA)
143           attributes.push_back(da.second);
144       }
145       return attributes;
146     }
147 
148     /// Construct a grid-function representing the cell-data with the given name
149     /// [[expects: read_ == true]]
getCellData(std::string const & name) const150     GridFunction<Vtk::CellContext> getCellData (std::string const& name) const
151     {
152       auto data_it = dataArray_.find("CellData." + name);
153       auto cell_it = cellData_.find("CellData." + name);
154       VTK_ASSERT_MSG(data_it != dataArray_.end() && cell_it != cellData_.end(),
155         "The data to extract is not found in cell-data. Try `getPointData()` instead!");
156       VTK_ASSERT(data_it->second.section == CELL_DATA);
157 
158       return {*creator_, cell_it->second, data_it->second.name, data_it->second.components,
159               data_it->second.type, vec_types, vec_offsets, vec_connectivity};
160     }
161 
162     /// Return a vector of DataArrayAttributes for all CELL_DATA blocks
163     /// [[expects: read_ == true]]
getCellDataAttributes() const164     std::vector<DataArrayAttributes> getCellDataAttributes () const
165     {
166       std::vector<DataArrayAttributes> attributes;
167       attributes.reserve(cellData_.size());
168       for (auto const& da : dataArray_) {
169         if (da.second.section == CELL_DATA)
170           attributes.push_back(da.second);
171       }
172       return attributes;
173     }
174 
175     /// Advanced read methods
176     /// @{
177 
178     /// Read the grid from an input stream, referring to a .vtu file, into the GridFactory \ref factory_
179     /**
180      * \param input   A STL input stream to read the VTK file from.
181      * \param create  If `false`, only fill internal data structures, if `true`, also create the grid. [true]
182      **/
183     void readSerialFileFromStream (std::ifstream& input, bool create = true);
184 
185     /// Read the grid from and input stream, referring to a .pvtu file, into the GridFactory \ref factory_
186     /**
187      * \param input   A STL input stream to read the VTK file from.
188      * \param create  If `false`, only fill internal data structures, if `true`, also create the grid. [true]
189      **/
190     void readParallelFileFromStream (std::ifstream& input, int rank, int size, bool create = true);
191 
192     /// Insert all internal data to the GridCreator
193     /// NOTE: requires an aforegoing call to \ref read()
194     void fillGridCreator (bool insertPieces = true);
195 
196     /// @}
197 
198     /// Return the filenames of parallel pieces
pieces() const199     std::vector<std::string> const& pieces () const
200     {
201       return pieces_;
202     }
203 
204 #ifndef DOXYGEN
205     // Implementation of the FileReader interface
fillFactoryImpl(GridFactory<Grid> & factory,std::string const & filename)206     static void fillFactoryImpl (GridFactory<Grid>& factory, std::string const& filename)
207     {
208       VtkReader reader{factory};
209       reader.read(filename);
210     }
211 #endif
212 
213   private:
214     // Read values stored on the cells with ID `id`
215     Sections readCellData (std::ifstream& input, std::string id);
216 
217     template <class F, class H>
218     void readCellDataAppended (MetaType<F>, MetaType<H>, std::ifstream& input, std::string id);
219 
220     // Read values stored on the points with ID `id`
221     Sections readPointData (std::ifstream& input, std::string id);
222 
223     template <class F, class H>
224     void readPointDataAppended (MetaType<F>, MetaType<H>, std::ifstream& input, std::string id);
225 
226 
227     // Read vertex coordinates from `input` stream and store in into `factory`
228     Sections readPoints (std::ifstream& input, std::string id);
229 
230     template <class F, class H>
231     void readPointsAppended (MetaType<F>, MetaType<H>, std::ifstream& input);
232 
233 
234     // Read cell type, cell offsets and connectivity from `input` stream
235     Sections readCells (std::ifstream& input, std::string id);
236 
237     template <class H>
238     void readCellsAppended (MetaType<H>, std::ifstream& input);
239 
240     // Read data from appended section in vtk file, starting from `offset`
241     template <class FloatType, class HeaderType>
242     void readAppended (std::ifstream& input, std::vector<FloatType>& values, HeaderType offset);
243 
244     // Test whether line belongs to section
isSection(std::string line,std::string key,Sections current,Sections parent=NO_SECTION) const245     bool isSection (std::string line,
246                     std::string key,
247                     Sections current,
248                     Sections parent = NO_SECTION) const
249     {
250       bool result = line.substr(1, key.length()) == key;
251       if (result && current != parent)
252         DUNE_THROW(Exception , "<" << key << "> in wrong section." );
253       return result;
254     }
255 
256     // Convert a section into a string
257     std::string toString (Sections s) const;
258 
259     // Find beginning of appended binary data
260     std::uint64_t findAppendedDataPosition (std::ifstream& input) const;
261 
262     // Read attributes from current xml tag
263     std::map<std::string, std::string> parseXml (std::string const& line, bool& closed);
264 
265     // clear all vectors
266     void clear ();
267 
comm() const268     auto comm () const
269     {
270       return MPIHelper::getCollectiveCommunication();
271     }
272 
273   private:
274     std::shared_ptr<GridCreator> creator_;
275 
276     /// Data format, i.e. ASCII, BINARY or COMPRESSED. Read from xml attributes.
277     Vtk::FormatTypes format_;
278 
279     /// Type of compression algorithm used for binary data
280     Vtk::CompressorTypes compressor_;
281 
282     // Temporary data to construct the grid elements
283     std::vector<GlobalCoordinate> vec_points;
284     std::vector<std::uint64_t> vec_point_ids;   //< Global unique vertex ID
285     std::vector<std::uint8_t> vec_types;        //< VTK cell type ID
286     std::vector<std::int64_t> vec_offsets;      //< offset of vertices of cell
287     std::vector<std::int64_t> vec_connectivity; //< vertex indices of cell
288 
289     std::size_t numberOfCells_ = 0;   //< Number of cells in the grid
290     std::size_t numberOfPoints_ = 0;  //< Number of vertices in the grid
291 
292     // offset information for appended data
293     // map: id -> {Name,DataType,NumberOfComponents,Offset}
294     std::map<std::string, DataArrayAttributes> dataArray_;
295 
296     // storage for internal point and cell data
297     // map: id -> vector of data entries per point/cell
298     std::map<std::string, std::vector<FieldType>> pointData_;
299     std::map<std::string, std::vector<FieldType>> cellData_;
300 
301     // vector of filenames of parallel pieces
302     std::vector<std::string> pieces_;
303 
304     /// Offset of beginning of appended data
305     std::uint64_t offset0_ = 0;
306 
307     bool read_ = false;
308   };
309 
310   // deduction guides
311   template <class Grid>
312   VtkReader (GridFactory<Grid>&)
313     -> VtkReader<Grid, Vtk::ContinuousGridCreator<Grid>>;
314 
315   template <class GridCreator,
316     class = std::void_t<typename GridCreator::Grid>>
317   VtkReader (GridCreator&)
318     -> VtkReader<typename GridCreator::Grid, GridCreator>;
319 
320   template <class GridCreator,
321     class = std::void_t<typename GridCreator::Grid>>
322   VtkReader (std::shared_ptr<GridCreator>)
323     -> VtkReader<typename GridCreator::Grid, GridCreator>;
324 
325 } // end namespace Dune
326 
327 #include "vtkreader.impl.hh"
328