1 // -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 // vi: set et ts=4 sw=2 sts=2:
3 
4 #ifndef DUNE_GRID_IO_FILE_VTK_BASICWRITER_HH
5 #define DUNE_GRID_IO_FILE_VTK_BASICWRITER_HH
6 
7 #include <fstream>
8 #include <iomanip>
9 #include <iterator>
10 #include <list>
11 #include <memory>
12 #include <sstream>
13 #include <string>
14 
15 #include <dune/common/parallel/mpiguard.hh>
16 #include <dune/common/path.hh>
17 
18 #include <dune/geometry/referenceelements.hh>
19 
20 #include <dune/grid/io/file/vtk/common.hh>
21 #include <dune/grid/io/file/vtk/functionwriter.hh>
22 #include <dune/grid/io/file/vtk/pvtuwriter.hh>
23 #include <dune/grid/io/file/vtk/vtuwriter.hh>
24 
25 namespace Dune
26 {
27   //! \addtogroup VTK
28   //! \{
29 
30   namespace VTK {
31 
32     template<typename IteratorFactory>
33     class BasicWriter {
34       typedef typename IteratorFactory::CellIterator CellIterator;
35       typedef typename IteratorFactory::CornerIterator CornerIterator;
36       typedef typename IteratorFactory::PointIterator PointIterator;
37 
38       typedef typename IteratorFactory::Cell Cell;
39 
40     public:
41       typedef FunctionWriterBase<Cell> FunctionWriter;
42 
43     private:
44       typedef std::list<std::shared_ptr<FunctionWriter> > WriterList;
45       typedef typename WriterList::const_iterator WIterator;
46 
47       typedef typename Cell::Geometry::ctype ctype;
48       static const unsigned celldim = Cell::mydimension;
49       typedef ReferenceElements<ctype, celldim> Refelems;
50 
51       static const FileType fileType = celldim == 1
52                                        ? polyData : unstructuredGrid;
53 
54       const IteratorFactory& factory;
55 
56       WriterList cellData;
57       WriterList pointData;
58 
59       CoordinatesWriter<Cell> coords;
60       typename IteratorFactory::ConnectivityWriter connectivity;
61       OffsetsWriter<Cell> offsets;
62       TypesWriter<Cell> types;
63 
64     public:
BasicWriter(const IteratorFactory & factory_)65       BasicWriter(const IteratorFactory& factory_)
66         : factory(factory_), connectivity(factory.makeConnectivity())
67       { }
68 
69       //////////////////////////////////////////////////////////////////////
70       //
71       //  Methods for adding data
72       //
73 
addCellData(const std::shared_ptr<FunctionWriter> & writer)74       void addCellData(const std::shared_ptr<FunctionWriter>& writer) {
75         cellData.push_back(writer);
76       }
77 
addPointData(const std::shared_ptr<FunctionWriter> & writer)78       void addPointData(const std::shared_ptr<FunctionWriter>& writer) {
79         pointData.push_back(writer);
80       }
81 
clear()82       void clear() {
83         cellData.clear();
84         pointData.clear();
85       }
86 
87     protected:
88       //////////////////////////////////////////////////////////////////////
89       //
90       //  Methods for writing single functions
91       //
92 
writeCellFunction(VTUWriter & vtuWriter,FunctionWriter & functionWriter,unsigned ncells) const93       void writeCellFunction(VTUWriter& vtuWriter,
94                              FunctionWriter& functionWriter,
95                              unsigned ncells) const
96       {
97         if(functionWriter.beginWrite(vtuWriter, ncells)) {
98           const CellIterator& cellend = factory.endCells();
99           for(CellIterator cellit = factory.beginCells(); cellit != cellend;
100               ++cellit)
101             functionWriter.write(*cellit, Refelems::general(cellit->type()).
102                                  position(0,0));
103         }
104         functionWriter.endWrite();
105       }
106 
writePointFunction(VTUWriter & vtuWriter,FunctionWriter & functionWriter,unsigned npoints) const107       void writePointFunction(VTUWriter& vtuWriter,
108                               FunctionWriter& functionWriter,
109                               unsigned npoints) const
110       {
111         if(functionWriter.beginWrite(vtuWriter, npoints)) {
112           const PointIterator& pend = factory.endPoints();
113           for(PointIterator pit = factory.beginPoints(); pit != pend; ++pit)
114             functionWriter.write(pit->cell(), pit->duneIndex());
115         }
116         functionWriter.endWrite();
117       }
118 
writeCornerFunction(VTUWriter & vtuWriter,FunctionWriter & functionWriter,unsigned ncorners) const119       void writeCornerFunction(VTUWriter& vtuWriter,
120                                FunctionWriter& functionWriter,
121                                unsigned ncorners) const
122       {
123         if(functionWriter.beginWrite(vtuWriter, ncorners)) {
124           const CornerIterator& cend = factory.endCorners();
125           for(CornerIterator cit = factory.beginCorners(); cit != cend; ++cit)
126             functionWriter.write(cit->cell(), cit->duneIndex());
127         }
128         functionWriter.endWrite();
129       }
130 
131       //////////////////////////////////////////////////////////////////////
132       //
133       //  Methods for writing whole sections
134       //
135 
getFirstScalar(const WriterList & data)136       static std::string getFirstScalar(const WriterList& data) {
137         const WIterator& wend = data.end();
138         for(WIterator wit = data.begin(); wit != wend; ++wit)
139           if((*wit)->ncomps() == 1)
140             return (*wit)->name();
141         return "";
142       }
143 
getFirstVector(const WriterList & data)144       static std::string getFirstVector(const WriterList& data) {
145         const WIterator& wend = data.end();
146         for(WIterator wit = data.begin(); wit != wend; ++wit)
147           if((*wit)->ncomps() == 3)
148             return (*wit)->name();
149         return "";
150       }
151 
writeCellData(VTUWriter & vtuWriter,unsigned ncells) const152       void writeCellData(VTUWriter& vtuWriter, unsigned ncells) const {
153         if(cellData.empty()) return;
154 
155         vtuWriter.beginCellData(getFirstScalar(cellData),
156                                 getFirstVector(cellData));
157         const WIterator& wend = cellData.end();
158         for(WIterator wit = cellData.begin(); wit != wend; ++wit)
159           writeCellFunction(vtuWriter, **wit, ncells);
160         vtuWriter.endCellData();
161       }
162 
writePointData(VTUWriter & vtuWriter,unsigned npoints) const163       void writePointData(VTUWriter& vtuWriter, unsigned npoints) const {
164         if(pointData.empty()) return;
165 
166         vtuWriter.beginPointData(getFirstScalar(pointData),
167                                  getFirstVector(pointData));
168         const WIterator& wend = pointData.end();
169         for(WIterator wit = pointData.begin(); wit != wend; ++wit)
170           writePointFunction(vtuWriter, **wit, npoints);
171         vtuWriter.endPointData();
172       }
173 
writeGrid(VTUWriter & vtuWriter,unsigned ncells,unsigned npoints,unsigned ncorners)174       void writeGrid(VTUWriter& vtuWriter, unsigned ncells, unsigned npoints,
175                      unsigned ncorners) {
176         vtuWriter.beginPoints();
177         writePointFunction(vtuWriter, coords, npoints);
178         vtuWriter.endPoints();
179 
180         vtuWriter.beginCells();
181         writeCornerFunction(vtuWriter, connectivity, ncorners);
182         writeCellFunction(vtuWriter, offsets, ncells);
183         if(fileType != polyData)
184           writeCellFunction(vtuWriter, types, ncells);
185         vtuWriter.endCells();
186       }
187 
writeAll(VTUWriter & vtuWriter,unsigned ncells,unsigned npoints,unsigned ncorners)188       void writeAll(VTUWriter& vtuWriter, unsigned ncells, unsigned npoints,
189                     unsigned ncorners) {
190         writeCellData(vtuWriter, ncells);
191         writePointData(vtuWriter, npoints);
192         writeGrid(vtuWriter, ncells, npoints, ncorners);
193       }
194 
195     public:
writePiece(const std::string & filename,OutputType outputType)196       void writePiece(const std::string& filename, OutputType outputType) {
197         std::ofstream stream;
198         stream.exceptions(std::ios_base::badbit | std::ios_base::failbit |
199                           std::ios_base::eofbit);
200         stream.open(filename.c_str(), std::ios::binary);
201 
202         VTUWriter vtuWriter(stream, outputType, fileType);
203 
204         unsigned ncells = std::distance(factory.beginCells(),
205                                         factory.endCells());
206         unsigned npoints = std::distance(factory.beginPoints(),
207                                          factory.endPoints());
208         unsigned ncorners = std::distance(factory.beginCorners(),
209                                           factory.endCorners());
210 
211         vtuWriter.beginMain(ncells, npoints);
212         writeAll(vtuWriter, ncells, npoints, ncorners);
213         vtuWriter.endMain();
214 
215         if(vtuWriter.beginAppended())
216           writeAll(vtuWriter, ncells, npoints, ncorners);
217         vtuWriter.endAppended();
218 
219       }
220 
221       //! write header file in parallel case to stream
222       /**
223        * Writes a .pvtu/.pvtp file for a collection of concurrently written
224        * .vtu/.vtp files.
225        *
226        * \param name      Name of file to write contents to,
227 
228        * \param piecename Base name of the pieces.  Should not contain a
229        *                  directory part or filename extension.
230        * \param piecepath Directory part of the pieces.  Since paraview does
231        *                  not support absolute paths in parallel collection
232        *                  files, this should be a path relative to the
233        *                  directory the collection file resides in.  A
234        *                  trailing '/' is optional, and an empty value "" is
235        *                  equivalent to the value "." except it will look
236        *                  nicer in the collection file.
237        */
writeCollection(const std::string name,const std::string & piecename,const std::string & piecepath)238       void writeCollection(const std::string name,
239                            const std::string& piecename,
240                            const std::string& piecepath)
241       {
242         std::ofstream stream;
243         stream.exceptions(std::ios_base::badbit | std::ios_base::failbit |
244                           std::ios_base::eofbit);
245         stream.open(name.c_str(), std::ios::binary);
246 
247         PVTUWriter writer(stream, fileType);
248 
249         writer.beginMain();
250 
251         // PPointData
252         writer.beginPointData(getFirstScalar(pointData),
253                               getFirstVector(pointData));
254         for(WIterator it=pointData.begin(); it!=pointData.end(); ++it)
255           (*it)->addArray(writer);
256         writer.endPointData();
257 
258         // PCellData
259         writer.beginCellData(getFirstScalar(cellData),
260                              getFirstVector(cellData));
261         for(WIterator it=cellData.begin(); it!=cellData.end(); ++it)
262           (*it)->addArray(writer);
263         writer.endCellData();
264 
265         // PPoints
266         writer.beginPoints();
267         coords.addArray(writer);
268         writer.endPoints();
269 
270         // Pieces
271         for( int i = 0; i < factory.comm().size(); ++i )
272           writer.addPiece(getParallelPieceName(piecename, piecepath, i));
273 
274         writer.endMain();
275       }
276 
277       //////////////////////////////////////////////////////////////////////
278       //
279       //  Filename generators
280       //
281 
282       //! return name of a parallel piece file
283       /**
284        * \param name Base name of the VTK output.  This should be without any
285        *             directory parts and without a filename extension.
286        * \param path Directory part of the resulting piece name.  May be
287        *             empty, in which case the resulting name will not have a
288        *             directory part.  If non-empty, may or may not have a
289        *             trailing '/'.  If a trailing slash is missing, one is
290        *             appended implicitly.
291        * \param rank Rank of the process to generate a piece name for.
292        */
getParallelPieceName(const std::string & name,const std::string & path,int rank) const293       std::string getParallelPieceName(const std::string& name,
294                                        const std::string& path, int rank) const
295       {
296         std::ostringstream s;
297         if(path.size() > 0) {
298           s << path;
299           if(path[path.size()-1] != '/')
300             s << '/';
301         }
302         s << 's' << std::setw(4) << std::setfill('0') << factory.comm().size()
303           << ':';
304         s << 'p' << std::setw(4) << std::setfill('0') << rank << ':';
305         s << name;
306         switch(fileType) {
307         case polyData :         s << ".vtp"; break;
308         case unstructuredGrid : s << ".vtu"; break;
309         }
310         return s.str();
311       }
312 
313       //! return name of a parallel header file
314       /**
315        * \param name Base name of the VTK output.  This should be without any
316        *             directory parts and without a filename extension.
317        * \param path Directory part of the resulting header name.  May be
318        *             empty, in which case the resulting name will not have a
319        *             directory part.  If non-empty, may or may not have a
320        *             trailing '/'.  If a trailing slash is missing, one is
321        *             appended implicitly.
322        */
getParallelHeaderName(const std::string & name,const std::string & path) const323       std::string getParallelHeaderName(const std::string& name,
324                                         const std::string& path) const
325       {
326         std::ostringstream s;
327         if(path.size() > 0) {
328           s << path;
329           if(path[path.size()-1] != '/')
330             s << '/';
331         }
332         s << 's' << std::setw(4) << std::setfill('0') << factory.comm().size()
333           << ':';
334         s << name;
335         switch(fileType) {
336         case polyData :         s << ".pvtp"; break;
337         case unstructuredGrid : s << ".pvtu"; break;
338         }
339         return s.str();
340       }
341 
342       //! return name of a serial piece file
343       /**
344        * This is similar to getParallelPieceName, but skips the prefixes for
345        * commSize ("s####:") and commRank ("p####:").
346        *
347        * \param name Base name of the VTK output.  This should be without any
348        *             directory parts and without a filename extension.
349        * \param path Directory part of the resulting piece name.  May be
350        *             empty, in which case the resulting name will not have a
351        *             directory part.  If non-empty, may or may not have a
352        *             trailing '/'.  If a trailing slash is missing, one is
353        *             appended implicitly.
354        */
getSerialPieceName(const std::string & name,const std::string & path) const355       std::string getSerialPieceName(const std::string& name,
356                                      const std::string& path) const
357       {
358         switch(fileType) {
359         case polyData :         return concatPaths(path, name+".vtp");
360         case unstructuredGrid : return concatPaths(path, name+".vtu");
361         }
362         return concatPaths(path, name); // unknown fileType
363       }
364 
365       //////////////////////////////////////////////////////////////////////
366       //
367       //  User interface functions for writing
368       //
369 
370       //! write output; interface might change later
371       /**
372        * \param name       Base name of the output files.  This should not
373        *                   contain any directory part and not filename
374        *                   extensions.  It will be used both for each processes
375        *                   piece as well as the parallel collection file.
376        * \param path       Directory where to put the parallel collection
377        *                   (.pvtu/.pvtp) file.  If it is relative, it is taken
378        *                   realtive to the current directory.
379        * \param extendpath Directory where to put the piece file (.vtu/.vtp) of
380        *                   this process.  If it is relative, it is taken
381        *                   relative to the directory denoted by path.
382        * \param outputType How to encode the data in the file.
383        *
384        * \note Currently, extendpath may not be absolute unless path is
385        *       absolute, because that would require the value of the current
386        *       directory.
387        *
388        * \throw NotImplemented Extendpath is absolute but path is relative.
389        * \throw IOError        Failed to open a file.
390        * \throw MPIGuardError  An exception was thrown during this method in
391        *                       one of the other processes.
392        */
pwrite(const std::string & name,const std::string & path,const std::string & extendpath,OutputType outputType)393       std::string pwrite(const std::string& name, const std::string& path,
394                          const std::string& extendpath, OutputType outputType)
395       {
396         MPIGuard guard(factory.comm());
397 
398         // do some magic because paraview can only cope with relative paths to
399         // piece files
400         std::ofstream file;
401         file.exceptions(std::ios_base::badbit | std::ios_base::failbit |
402                         std::ios_base::eofbit);
403         std::string piecepath = concatPaths(path, extendpath);
404         std::string relpiecepath = relativePath(path, piecepath);
405 
406         // write this processes .vtu/.vtp piece file
407         std::string fullname = getParallelPieceName(name, piecepath,
408                                                     factory.comm().rank());
409         writePiece(fullname, outputType);
410 
411         // if we are rank 0, write .pvtu/.pvtp parallel header
412         fullname = getParallelHeaderName(name, path);
413         if(factory.comm().rank() == 0)
414           writeCollection(fullname, name, relpiecepath);
415 
416         guard.finalize();
417 
418         return fullname;
419       }
420 
421       /** \brief write output (interface might change later)
422        *
423        *  This method can  be used in parallel as well  as in serial programs.
424        *  For  serial runs  (commSize=1) it  chooses other  names  without the
425        *  "s####:p####:" prefix  for the .vtu/.vtp files and  omits writing of
426        *  the .pvtu/pvtp file however.  For parallel runs (commSize > 1) it is
427        *  the same as a call to pwrite() with path="" and extendpath="".
428        *
429        *  \param name     Base name of the output files.  This should not
430        *                  contain any directory part and no filename
431        *                  extensions.
432        *  \param outputType How to encode the data in the file.
433        */
write(const std::string & name,OutputType outputType)434       std::string write(const std::string &name, OutputType outputType)
435       {
436         // in the parallel case, just use pwrite, it has all the necessary
437         // stuff, so we don't need to reimplement it here.
438         if(factory.comm().size() > 1)
439           return pwrite(name, "", "", outputType);
440 
441         // generate filename for process data
442         std::string pieceName = getSerialPieceName(name, "");
443 
444         writePiece(pieceName, outputType);
445 
446         return pieceName;
447       }
448 
449     };
450 
451   } // namespace VTK
452 
453   //! \} group VTK
454 
455 } // namespace Dune
456 
457 #endif // DUNE_GRID_IO_FILE_VTK_BASICWRITER_HH
458