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