diff --git a/dune/tectonic/myvtkwriter.hh b/dune/tectonic/myvtkwriter.hh new file mode 100644 index 0000000000000000000000000000000000000000..dea55cb4f3c923ad33d4d944af952884dafd3448 --- /dev/null +++ b/dune/tectonic/myvtkwriter.hh @@ -0,0 +1,1006 @@ +// copied from <dune/grid/io/file/vtk/vtkwriter.hh> + +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifndef DUNE_TECTONIC_VTKWRITER_HH +#define DUNE_TECTONIC_VTKWRITER_HH + +#include <cstring> +#include <iostream> +#include <string> +#include <fstream> +#include <sstream> +#include <iomanip> + +#include <vector> +#include <list> + +#include <dune/common/deprecated.hh> +#include <dune/common/exceptions.hh> +#include <dune/common/indent.hh> +#include <dune/common/iteratorfacades.hh> +#include <dune/common/path.hh> +#include <dune/common/shared_ptr.hh> +#include <dune/geometry/referenceelements.hh> +#include <dune/grid/common/mcmgmapper.hh> +#include <dune/grid/common/gridenums.hh> +#include <dune/grid/io/file/vtk/common.hh> +#include <dune/grid/io/file/vtk/dataarraywriter.hh> +#include <dune/grid/io/file/vtk/function.hh> +#include <dune/grid/io/file/vtk/pvtuwriter.hh> +#include <dune/grid/io/file/vtk/streams.hh> +#include <dune/grid/io/file/vtk/vtuwriter.hh> + +/** @file + @author Peter Bastian, Christian Engwer + @brief Provides file i/o for the visualization toolkit +*/ + +/** + @todo put vtk io intro here ... + + details and examples regarding the VTK file format can be found here: + + http://www.geophysik.uni-muenchen.de/intranet/it-service/applications/paraview/vtk-file-formats/ + (not available any more) + + http://www.geophysik.uni-muenchen.de/~moder/Paraview/VTK_File_Formats.php + (alternative) +*/ + +namespace Dune { +/** + * @brief Writer for the ouput of grid functions in the vtk format. + * @ingroup VTK + * + * Writes arbitrary grid functions (living on cells or vertices of a grid) + * to a file suitable for easy visualization with + * <a href="http://public.kitware.com/VTK/">The Visualization Toolkit (VTK)</a>. + */ +template <class GridView> class MyVTKWriter { + // extract types + typedef typename GridView::Grid Grid; + typedef typename GridView::ctype DT; + enum { + n = GridView::dimension + }; + enum { + w = GridView::dimensionworld + }; + + typedef typename GridView::template Codim<0>::Entity Cell; + typedef typename GridView::template Codim<n>::Entity Vertex; + typedef Cell Entity; + + typedef typename GridView::IndexSet IndexSet; + + static const PartitionIteratorType VTK_Partition = InteriorBorder_Partition; + + typedef typename GridView::template Codim<0>::template Partition< + VTK_Partition>::Iterator GridCellIterator; + typedef typename GridView::template Codim<n>::template Partition< + VTK_Partition>::Iterator GridVertexIterator; + + typedef MultipleCodimMultipleGeomTypeMapper<GridView, MCMGVertexLayout> + VertexMapper; + +public: + typedef Dune::VTKFunction<GridView> VTKFunction; + typedef shared_ptr<VTKFunction> VTKFunctionPtr; + +protected: + typedef typename std::list<VTKFunctionPtr>::const_iterator FunctionIterator; + + //! Iterator over the grids elements + /** + * This class iterates over the gridview's elements. It is the same as + * the gridview's Codim<0>::Iterator for the InteriorBorder_Partition, + * except that it add a position() method. + */ + class CellIterator : public GridCellIterator { + public: + //! construct a CellIterator from the gridview's Iterator. + CellIterator(const GridCellIterator& x) : GridCellIterator(x) {}; + //! get the position of the center of the element, in element-local + //! coordinates + const FieldVector<DT, n> position() const { + return GenericReferenceElements<DT, n>::general((*this)->type()) + .position(0, 0); + } + }; + + CellIterator cellBegin() const { + return gridView_.template begin<0, VTK_Partition>(); + } + + CellIterator cellEnd() const { + return gridView_.template end<0, VTK_Partition>(); + } + + //! Iterate over the grid's vertices + /** + * This class iterates over the elements, and within the elements over the + * corners. If the data mode dm is nonconforming, each vertex is visited + * once for each element where it is a corner (similar to CornerIterator). + * If dm is conforming each vertex is visited only once globally, for the + * first element where it is a corner. Contrary to CornerIterator, visit + * the corners of a given element in Dune-ordering. + * + * Dereferencing the iterator yields the current entity, and the index of + * the current corner within that entity is returned by the iterators + * localindex() method. Another useful method on the iterator itself is + * position() which returns the element-local position of the current + * corner. + */ + class VertexIterator : public ForwardIteratorFacade< + VertexIterator, const Entity, const Entity&, int> { + GridCellIterator git; + GridCellIterator gend; + VTK::DataMode datamode; + // Index of the currently visited corner within the current element. + // NOTE: this is in Dune-numbering, in contrast to CornerIterator. + int cornerIndexDune; + const VertexMapper& vertexmapper; + std::vector<bool> visited; + // in conforming mode, for each vertex id (as obtained by vertexmapper) + // hold its number in the iteration order (VertexIterator) + int offset; + + protected: + void basicIncrement() { + if (git == gend) + return; + ++cornerIndexDune; + const int numCorners = git->template count<n>(); + if (cornerIndexDune == numCorners) { + offset += numCorners; + cornerIndexDune = 0; + + ++git; + while ((git != gend) && (git->partitionType() != InteriorEntity)) + ++git; + } + } + + public: + VertexIterator(const GridCellIterator& x, const GridCellIterator& end, + const VTK::DataMode& dm, const VertexMapper& vm) + : git(x), + gend(end), + datamode(dm), + cornerIndexDune(0), + vertexmapper(vm), + visited(vm.size(), false), + offset(0) { + if (datamode == VTK::conforming && git != gend) + visited[vertexmapper.map(*git, cornerIndexDune, n)] = true; + }; + void increment() { + switch (datamode) { + case VTK::conforming: + while (visited[vertexmapper.map(*git, cornerIndexDune, n)]) { + basicIncrement(); + if (git == gend) + return; + } + visited[vertexmapper.map(*git, cornerIndexDune, n)] = true; + break; + case VTK::nonconforming: + basicIncrement(); + break; + } + } + bool equals(const VertexIterator& cit) const { + return git == cit.git && cornerIndexDune == cit.cornerIndexDune && + datamode == cit.datamode; + } + const Entity& dereference() const { return *git; } + //! index of vertex within the entity, in Dune-numbering + int localindex() const { return cornerIndexDune; } + //! position of vertex inside the entity + const FieldVector<DT, n>& position() const { + return GenericReferenceElements<DT, n>::general(git->type()) + .position(cornerIndexDune, n); + } + }; + + VertexIterator vertexBegin() const { + return VertexIterator(gridView_.template begin<0, VTK_Partition>(), + gridView_.template end<0, VTK_Partition>(), datamode, + *vertexmapper); + } + + VertexIterator vertexEnd() const { + return VertexIterator(gridView_.template end<0, VTK_Partition>(), + gridView_.template end<0, VTK_Partition>(), datamode, + *vertexmapper); + } + + //! Iterate over the elements' corners + /** + * This class iterates over the elements, and within the elements over the + * corners. Each vertex in the grid can be a corner in multiple elements, + * and is visited once for each element it is associated with. This class + * differs from VertexIterator in that it visits the corners of a given + * element in VTK-ordering, and that it always visits a given vertex once + * for each element where that vertex is a corner in, independent of the + * data mode dm. + * + * Dereferencing the iterator yields the current entity. Another useful + * method on the iterator itself is id(), which returns the number of the + * current corners associated vertex, in the numbering given by the + * iteration order of VertexIterator. + */ + class CornerIterator : public ForwardIteratorFacade< + CornerIterator, const Entity, const Entity&, int> { + GridCellIterator git; + GridCellIterator gend; + VTK::DataMode datamode; + // Index of the currently visited corner within the current element. + // NOTE: this is in VTK-numbering, in contrast to VertexIterator. + int cornerIndexVTK; + const VertexMapper& vertexmapper; + // in conforming mode, for each vertex id (as obtained by vertexmapper) + // hold its number in the iteration order of VertexIterator (*not* + // CornerIterator) + const std::vector<int>& number; + // holds the number of corners of all the elements we have seen so far, + // excluding the current element + int offset; + + public: + CornerIterator(const GridCellIterator& x, const GridCellIterator& end, + const VTK::DataMode& dm, const VertexMapper& vm, + const std::vector<int>& num) + : git(x), + gend(end), + datamode(dm), + cornerIndexVTK(0), + vertexmapper(vm), + number(num), + offset(0) {}; + void increment() { + if (git == gend) + return; + ++cornerIndexVTK; + const int numCorners = git->template count<n>(); + if (cornerIndexVTK == numCorners) { + offset += numCorners; + cornerIndexVTK = 0; + + ++git; + while ((git != gend) && (git->partitionType() != InteriorEntity)) + ++git; + } + } + bool equals(const CornerIterator& cit) const { + return git == cit.git && cornerIndexVTK == cit.cornerIndexVTK && + datamode == cit.datamode; + } + const Entity& dereference() const { return *git; } + //! Process-local consecutive zero-starting vertex id + /** + * This method returns the number of this corners associated vertex, in + * the numbering given by the iteration order of VertexIterator. + */ + int id() const { + switch (datamode) { + case VTK::conforming: + return number[vertexmapper.map( + *git, VTK::renumber(*git, cornerIndexVTK), n)]; + case VTK::nonconforming: + return offset + VTK::renumber(*git, cornerIndexVTK); + default: + DUNE_THROW(IOError, "MyVTKWriter: unsupported DataMode" << datamode); + } + } + }; + + CornerIterator cornerBegin() const { + return CornerIterator(gridView_.template begin<0, VTK_Partition>(), + gridView_.template end<0, VTK_Partition>(), datamode, + *vertexmapper, number); + } + + CornerIterator cornerEnd() const { + return CornerIterator(gridView_.template end<0, VTK_Partition>(), + gridView_.template end<0, VTK_Partition>(), datamode, + *vertexmapper, number); + } + +public: + /** + * @brief Construct a MyVTKWriter working on a specific GridView. + * + * + * @param gridView The gridView the grid functions live on. (E. g. a + *LevelGridView.) + * @param dm The data mode. + */ + explicit MyVTKWriter(const GridView& gridView, + VTK::DataMode dm = VTK::conforming) + : gridView_(gridView), datamode(dm) {} + + /** + * @brief Add a grid function that lives on the cells of the grid to the + * visualization. + * @param p Dune::shared_ptr to the function to visualize + */ + void addCellData(const VTKFunctionPtr& p) { celldata.push_back(p); } + + /** + * @brief Add a grid function that lives on the cells of the grid to the + * visualization. + * @param p The function to visualize. The MyVTKWriter object will take + * ownership of the VTKFunction *p and delete it when it's done. + */ + void addCellData(VTKFunction* p) // DUNE_DEPRECATED + { + celldata.push_back(VTKFunctionPtr(p)); + } + + /** + * @brief Add a grid function (represented by container) that lives on the + *cells of + * the grid to the visualization. + * + * The container has to have random access via operator[] (e. g. std::vector). + *The + * value of the grid function for an arbitrary element + * will be accessed by calling operator[] with the index (corresponding + * with the grid view) of the element. + * For vector valued data all components for an element are assumed to + * be consecutive. + * + * @param v The container with the values of the grid function for each cell. + * @param name A name to identify the grid function. + * @param ncomps Number of components (default is 1). + */ + template <class V> + void addCellData(const V& v, const std::string& name, int ncomps = 1) { + typedef P0VTKFunction<GridView, V> Function; + for (int c = 0; c < ncomps; ++c) { + std::stringstream compName; + compName << name; + if (ncomps > 1) + compName << "[" << c << "]"; + VTKFunction* p = new Function(gridView_, v, compName.str(), ncomps, c); + celldata.push_back(VTKFunctionPtr(p)); + } + } + + /** + * @brief Add a grid function that lives on the vertices of the grid to the + * visualization. + * @param p The function to visualize. The MyVTKWriter object will take + * ownership of the VTKFunction *p and delete it when it's done. + */ + void addVertexData(VTKFunction* p) // DUNE_DEPRECATED + { + vertexdata.push_back(VTKFunctionPtr(p)); + } + + /** + * @brief Add a grid function that lives on the vertices of the grid to the + * visualization. + * @param p Dune::shared_ptr to the function to visualize + */ + void addVertexData(const VTKFunctionPtr& p) { vertexdata.push_back(p); } + + /** + * @brief Add a grid function (represented by container) that lives on the + *vertices of the + * grid to the visualization output. + * + * The container has to have random access via operator[] (e. g. std::vector). + *The value + * of the grid function for an arbitrary element + * will be accessed by calling operator[] with the index (corresponding + * to the grid view) of the vertex. + * For vector valued data all components for a vertex are assumed to + * be consecutive. + * + * @param v The container with the values of the grid function for each cell. + * @param name A name to identify the grid function. + * @param ncomps Number of components (default is 1). + */ + template <class V> + void addVertexData(const V& v, const std::string& name, int ncomps = 1) { + typedef P1VTKFunction<GridView, V> Function; + for (int c = 0; c < ncomps; ++c) { + std::stringstream compName; + compName << name; + if (ncomps > 1) + compName << "[" << c << "]"; + VTKFunction* p = new Function(gridView_, v, compName.str(), ncomps, c); + vertexdata.push_back(VTKFunctionPtr(p)); + } + } + + //! clear list of registered functions + void clear() { + celldata.clear(); + vertexdata.clear(); + } + + //! destructor + virtual ~MyVTKWriter() { this->clear(); } + + /** \brief write output (interface might change later) + * + * This method can be used in parallel as well as in serial programs. + * For serial runs (commSize=1) it chooses other names without the + * "s####:p####:" prefix for the .vtu/.vtp files and omits writing of the + * .pvtu/pvtp file however. For parallel runs (commSize > 1) it is the + * same as a call to pwrite() with path="" and extendpath="". + * + * \param[in] name basic name to write (may not contain a path) + * \param[in] type type of output (e.g,, ASCII) (optional) + */ + std::string write(const std::string& name, + VTK::OutputType type = VTK::ascii) { + return write(name, type, gridView_.comm().rank(), gridView_.comm().size()); + } + + /** \brief write output (interface might change later) + * + * "pwrite" means "path write" (i.e. write somewhere else than the current + * directory). The "p" does not mean this method has a monopoly on + * parallel writing, the regular write(const std::string &, + * VTK::OutputType) method can do that just fine. + * + * \param name Base name of the output files. This should not + * contain any directory part and not filename + * extensions. It will be used both for each processes + * piece as well as the parallel collection file. + * \param path Directory where to put the parallel collection + * (.pvtu/.pvtp) file. If it is relative, it is taken + * realtive to the current directory. + * \param extendpath Directory where to put the piece file (.vtu/.vtp) of + * this process. If it is relative, it is taken + * relative to the directory denoted by path. + * \param type How to encode the data in the file. + * + * \note Currently, extendpath may not be absolute unless path is + * absolute, because that would require the value of the current + * directory. + * + * \throw NotImplemented Extendpath is absolute but path is relative. + * \throw IOError Failed to open a file. + */ + std::string pwrite(const std::string& name, const std::string& path, + const std::string& extendpath, + VTK::OutputType type = VTK::ascii) { + return pwrite(name, path, extendpath, type, gridView_.comm().rank(), + gridView_.comm().size()); + } + +protected: + //! return name of a parallel piece file + /** + * \param name Base name of the VTK output. This should be without + * any directory parts and without a filename extension. + * \param path Directory part of the resulting piece name. May be + * empty, in which case the resulting name will not have a + * directory part. If non-empty, may or may not have a + * trailing '/'. If a trailing slash is missing, one is + * appended implicitly. + * \param commRank Rank of the process to generate a piece name for. + * \param commSize Number of processes writing a parallel vtk output. + */ + std::string getParallelPieceName(const std::string& name, + const std::string& path, int commRank, + int commSize) const { + std::ostringstream s; + if (path.size() > 0) { + s << path; + if (path[path.size() - 1] != '/') + s << '/'; + } + s << 's' << std::setw(4) << std::setfill('0') << commSize << ':'; + s << 'p' << std::setw(4) << std::setfill('0') << commRank << ':'; + s << name; + if (GridView::dimension > 1) + s << ".vtu"; + else + s << ".vtp"; + return s.str(); + } + + //! return name of a parallel header file + /** + * \param name Base name of the VTK output. This should be without + * any directory parts and without a filename extension. + * \param path Directory part of the resulting header name. May be + * empty, in which case the resulting name will not have a + * directory part. If non-empty, may or may not have a + * trailing '/'. If a trailing slash is missing, one is + * appended implicitly. + * \param commSize Number of processes writing a parallel vtk output. + */ + std::string getParallelHeaderName(const std::string& name, + const std::string& path, + int commSize) const { + std::ostringstream s; + if (path.size() > 0) { + s << path; + if (path[path.size() - 1] != '/') + s << '/'; + } + s << 's' << std::setw(4) << std::setfill('0') << commSize << ':'; + s << name; + if (GridView::dimension > 1) + s << ".pvtu"; + else + s << ".pvtp"; + return s.str(); + } + + //! return name of a serial piece file + /** + * This is similar to getParallelPieceName, but skips the prefixes for + * commSize ("s####:") and commRank ("p####:"). + * + * \param name Base name of the VTK output. This should be without + * any directory parts and without a filename extension. + * \param path Directory part of the resulting piece name. May be + * empty, in which case the resulting name will not have a + * directory part. If non-empty, may or may not have a + * trailing '/'. If a trailing slash is missing, one is + * appended implicitly. + */ + std::string getSerialPieceName(const std::string& name, + const std::string& path) const { + static const std::string extension = + GridView::dimension == 1 ? ".vtp" : ".vtu"; + + return concatPaths(path, name + extension); + } + + /** \brief write output (interface might change later) + * + * This method can be used in parallel as well as in serial programs. + * For serial runs (commSize=1) it chooses other names without the + * "s####:p####:" prefix for the .vtu/.vtp files and omits writing of the + * .pvtu/pvtp file however. For parallel runs (commSize > 1) it is the + * same as a call to pwrite() with path="" and extendpath="". + * + * \param name Base name of the output files. This should not + * contain any directory part and no filename extensions. + * \param type How to encode the data in the file. + * \param commRank Rank of the current process. + * \param commSize Number of processes taking part in this write + * operation. + */ + std::string write(const std::string& name, VTK::OutputType type, + const int commRank, const int commSize) { + // in the parallel case, just use pwrite, it has all the necessary + // stuff, so we don't need to reimplement it here. + if (commSize > 1) + return pwrite(name, "", "", type, commRank, commSize); + + // make data mode visible to private functions + outputtype = type; + + // generate filename for process data + std::string pieceName = getSerialPieceName(name, ""); + + // write process data + std::ofstream file; + file.open(pieceName.c_str(), std::ios::binary); + if (!file.is_open()) + DUNE_THROW(IOError, "Could not write to piece file " << pieceName); + writeDataFile(file); + file.close(); + + return pieceName; + } + + //! write output; interface might change later + /** + * \param name Base name of the output files. This should not + * contain any directory part and not filename + * extensions. It will be used both for each processes + * piece as well as the parallel collection file. + * \param path Directory where to put the parallel collection + * (.pvtu/.pvtp) file. If it is relative, it is taken + * realtive to the current directory. + * \param extendpath Directory where to put the piece file (.vtu/.vtp) of + * this process. If it is relative, it is taken + * relative to the directory denoted by path. + * \param ot How to encode the data in the file. + * \param commRank Rank of the current process. + * \param commSize Number of processes taking part in this write + * operation. + * + * \note Currently, extendpath may not be absolute unless path is + * absolute, because that would require the value of the current + * directory. + * + * \throw NotImplemented Extendpath is absolute but path is relative. + * \throw IOError Failed to open a file. + */ + std::string pwrite(const std::string& name, const std::string& path, + const std::string& extendpath, VTK::OutputType ot, + const int commRank, const int commSize) { + // make data mode visible to private functions + outputtype = ot; + + // do some magic because paraview can only cope with relative pathes to + // piece files + std::ofstream file; + std::string piecepath = concatPaths(path, extendpath); + std::string relpiecepath = relativePath(path, piecepath); + + // write this processes .vtu/.vtp piece file + std::string fullname = + getParallelPieceName(name, piecepath, commRank, commSize); + file.open(fullname.c_str(), std::ios::binary); + if (!file.is_open()) + DUNE_THROW(IOError, "Could not write to piecefile file " << fullname); + writeDataFile(file); + file.close(); + gridView_.comm().barrier(); + + // if we are rank 0, write .pvtu/.pvtp parallel header + fullname = getParallelHeaderName(name, path, commSize); + if (commRank == 0) { + file.open(fullname.c_str()); + if (!file.is_open()) + DUNE_THROW(IOError, "Could not write to parallel file " << fullname); + writeParallelHeader(file, name, relpiecepath, commSize); + file.close(); + } + gridView_.comm().barrier(); + return fullname; + } + +private: + //! write header file in parallel case to stream + /** + * Writes a .pvtu/.pvtp file for a collection of concurrently written + * .vtu/.vtp files. + * + * \param s Stream to write the file contents to. + * \param piecename Base name of the pieces. Should not contain a + * directory part or filename extension. + * \param piecepath Directory part of the pieces. Since paraview does not + * support absolute paths in parallel collection files, + * this should be a path relative to the directory the + * collection file resides in. A trailing '/' is + * optional, and an empty value "" is equivalent to the + * value "." except it will look nicer in the collection + * file. + * \param commSize Number of processes which are producing the VTK + * output. + */ + void writeParallelHeader(std::ostream& s, const std::string& piecename, + const std::string& piecepath, const int commSize) { + VTK::FileType fileType = (n == 1) ? VTK::polyData : VTK::unstructuredGrid; + + VTK::PVTUWriter writer(s, fileType); + + writer.beginMain(); + + // PPointData + { + std::string scalars; + for (FunctionIterator it = vertexdata.begin(); it != vertexdata.end(); + ++it) + if ((*it)->ncomps() == 1) { + scalars = (*it)->name(); + break; + } + std::string vectors; + for (FunctionIterator it = vertexdata.begin(); it != vertexdata.end(); + ++it) + if ((*it)->ncomps() > 1) { + vectors = (*it)->name(); + break; + } + writer.beginPointData(scalars, vectors); + } + for (FunctionIterator it = vertexdata.begin(); it != vertexdata.end(); + ++it) { + unsigned writecomps = (*it)->ncomps(); + if (writecomps == 2) + writecomps = 3; + writer.addArray<float>((*it)->name(), writecomps); + } + writer.endPointData(); + + // PCellData + { + std::string scalars; + for (FunctionIterator it = celldata.begin(); it != celldata.end(); ++it) + if ((*it)->ncomps() == 1) { + scalars = (*it)->name(); + break; + } + std::string vectors; + for (FunctionIterator it = celldata.begin(); it != celldata.end(); ++it) + if ((*it)->ncomps() > 1) { + vectors = (*it)->name(); + break; + } + writer.beginCellData(scalars, vectors); + } + for (FunctionIterator it = celldata.begin(); it != celldata.end(); ++it) { + unsigned writecomps = (*it)->ncomps(); + if (writecomps == 2) + writecomps = 3; + writer.addArray<float>((*it)->name(), writecomps); + } + writer.endCellData(); + + // PPoints + writer.beginPoints(); + writer.addArray<float>("Coordinates", 3); + writer.endPoints(); + + // Pieces + for (int i = 0; i < commSize; ++i) { + const std::string& fullname = + getParallelPieceName(piecename, piecepath, i, commSize); + writer.addPiece(fullname); + } + + writer.endMain(); + } + + //! write data file to stream + void writeDataFile(std::ostream& s) { + VTK::FileType fileType = (n == 1) ? VTK::polyData : VTK::unstructuredGrid; + + VTK::VTUWriter writer(s, outputtype, fileType); + + // Grid characteristics + vertexmapper = new VertexMapper(gridView_); + if (datamode == VTK::conforming) { + number.resize(vertexmapper->size()); + for (std::vector<int>::size_type i = 0; i < number.size(); i++) + number[i] = -1; + } + countEntities(nvertices, ncells, ncorners); + + writer.beginMain(ncells, nvertices); + writeAllData(writer); + writer.endMain(); + + // write appended binary data section + if (writer.beginAppended()) + writeAllData(writer); + writer.endAppended(); + + delete vertexmapper; + number.clear(); + } + + void writeAllData(VTK::VTUWriter& writer) { + // PointData + writeVertexData(writer); + + // CellData + writeCellData(writer); + + // Points + writeGridPoints(writer); + + // Cells + writeGridCells(writer); + } + +protected: + std::string getFormatString() const { + if (outputtype == VTK::ascii) + return "ascii"; + if (outputtype == VTK::base64) + return "binary"; + if (outputtype == VTK::appendedraw) + return "appended"; + if (outputtype == VTK::appendedbase64) + return "appended"; + DUNE_THROW(IOError, "MyVTKWriter: unsupported OutputType" << outputtype); + } + + std::string getTypeString() const { + if (n == 1) + return "PolyData"; + else + return "UnstructuredGrid"; + } + + //! count the vertices, cells and corners + virtual void countEntities(int& nvertices, int& ncells, int& ncorners) { + nvertices = 0; + ncells = 0; + ncorners = 0; + for (CellIterator it = cellBegin(); it != cellEnd(); ++it) { + ncells++; + // because of the use of vertexmapper->map(), this iteration must be + // in the order of Dune's numbering. + for (int i = 0; i < it->template count<n>(); ++i) { + ncorners++; + if (datamode == VTK::conforming) { + int alpha = vertexmapper->map(*it, i, n); + if (number[alpha] < 0) + number[alpha] = nvertices++; + } else { + nvertices++; + } + } + } + } + + //! write cell data + virtual void writeCellData(VTK::VTUWriter& writer) { + if (celldata.size() == 0) + return; + + std::string scalars = ""; + for (FunctionIterator it = celldata.begin(); it != celldata.end(); ++it) + if ((*it)->ncomps() == 1) { + scalars = (*it)->name(); + break; + } + std::string vectors = ""; + for (FunctionIterator it = celldata.begin(); it != celldata.end(); ++it) + if ((*it)->ncomps() > 1) { + vectors = (*it)->name(); + break; + } + + writer.beginCellData(scalars, vectors); + for (FunctionIterator it = celldata.begin(); it != celldata.end(); ++it) { + // vtk file format: a vector data always should have 3 comps (with + // 3rd comp = 0 in 2D case) + unsigned writecomps = (*it)->ncomps(); + if (writecomps == 2) + writecomps = 3; + shared_ptr<VTK::DataArrayWriter<float>> p( + writer.makeArrayWriter<float>((*it)->name(), writecomps, ncells)); + if (!p->writeIsNoop()) + for (CellIterator i = cellBegin(); i != cellEnd(); ++i) { + for (int j = 0; j < (*it)->ncomps(); j++) + p->write((*it)->evaluate(j, *i, i.position())); + // vtk file format: a vector data always should have 3 comps + // (with 3rd comp = 0 in 2D case) + for (unsigned j = (*it)->ncomps(); j < writecomps; ++j) + p->write(0.0); + } + } + writer.endCellData(); + } + + //! write vertex data + virtual void writeVertexData(VTK::VTUWriter& writer) { + if (vertexdata.size() == 0) + return; + + std::string scalars = ""; + for (FunctionIterator it = vertexdata.begin(); it != vertexdata.end(); ++it) + if ((*it)->ncomps() == 1) { + scalars = (*it)->name(); + break; + } + std::string vectors = ""; + for (FunctionIterator it = vertexdata.begin(); it != vertexdata.end(); ++it) + if ((*it)->ncomps() > 1) { + vectors = (*it)->name(); + break; + } + + writer.beginPointData(scalars, vectors); + for (FunctionIterator it = vertexdata.begin(); it != vertexdata.end(); + ++it) { + // vtk file format: a vector data always should have 3 comps (with + // 3rd comp = 0 in 2D case) + unsigned writecomps = (*it)->ncomps(); + if (writecomps == 2) + writecomps = 3; + shared_ptr<VTK::DataArrayWriter<float>> p( + writer.makeArrayWriter<float>((*it)->name(), writecomps, nvertices)); + if (!p->writeIsNoop()) + for (VertexIterator vit = vertexBegin(); vit != vertexEnd(); ++vit) { + for (int j = 0; j < (*it)->ncomps(); j++) + p->write((*it)->evaluate(j, *vit, vit.position())); + // vtk file format: a vector data always should have 3 comps + // (with 3rd comp = 0 in 2D case) + for (unsigned j = (*it)->ncomps(); j < writecomps; ++j) + p->write(0.0); + } + } + writer.endPointData(); + } + + //! write the positions of vertices + virtual void writeGridPoints(VTK::VTUWriter& writer) { + writer.beginPoints(); + + shared_ptr<VTK::DataArrayWriter<float>> p( + writer.makeArrayWriter<float>("Coordinates", 3, nvertices)); + if (!p->writeIsNoop()) { + VertexIterator vEnd = vertexEnd(); + for (VertexIterator vit = vertexBegin(); vit != vEnd; ++vit) { + int dimw = w; + for (int j = 0; j < std::min(dimw, 3); j++) + p->write(vit->geometry().corner(vit.localindex())[j]); + for (int j = std::min(dimw, 3); j < 3; j++) + p->write(0.0); + } + } + // free the VTK::DataArrayWriter before touching the stream + p.reset(); + + writer.endPoints(); + } + + //! write the connectivity array + virtual void writeGridCells(VTK::VTUWriter& writer) { + writer.beginCells(); + + // connectivity + { + shared_ptr<VTK::DataArrayWriter<int>> p1( + writer.makeArrayWriter<int>("connectivity", 1, ncorners)); + if (!p1->writeIsNoop()) + for (CornerIterator it = cornerBegin(); it != cornerEnd(); ++it) + p1->write(it.id()); + } + + // offsets + { + shared_ptr<VTK::DataArrayWriter<int>> p2( + writer.makeArrayWriter<int>("offsets", 1, ncells)); + if (!p2->writeIsNoop()) { + int offset = 0; + for (CellIterator it = cellBegin(); it != cellEnd(); ++it) { + offset += it->template count<n>(); + p2->write(offset); + } + } + } + + // types + if (n > 1) { + shared_ptr<VTK::DataArrayWriter<unsigned char>> p3( + writer.makeArrayWriter<unsigned char>("types", 1, ncells)); + if (!p3->writeIsNoop()) + for (CellIterator it = cellBegin(); it != cellEnd(); ++it) { + int vtktype = VTK::geometryType(it->type()); + p3->write(vtktype); + } + } + + writer.endCells(); + } + +protected: + // the list of registered functions + std::list<VTKFunctionPtr> celldata; + std::list<VTKFunctionPtr> vertexdata; + +private: + // the grid + GridView gridView_; + + // temporary grid information +protected: + int ncells; + int nvertices; + int ncorners; + +private: + VertexMapper* vertexmapper; + // in conforming mode, for each vertex id (as obtained by vertexmapper) + // hold its number in the iteration order (VertexIterator) + std::vector<int> number; + VTK::DataMode datamode; + +protected: + VTK::OutputType outputtype; +}; +} + +#endif diff --git a/src/one-body-sample.cc b/src/one-body-sample.cc index 5cc4c25008e1c90c392d292ca79f6ac283cf3056..35b4d2f3188082ab0195b3a87b4f842486435041 100644 --- a/src/one-body-sample.cc +++ b/src/one-body-sample.cc @@ -20,7 +20,6 @@ #include <dune/common/shared_ptr.hh> #include <dune/common/stdstreams.hh> #include <dune/grid/common/mcmgmapper.hh> -#include <dune/grid/io/file/vtk/vtkwriter.hh> #include <dune/grid/yaspgrid.hh> #include <dune/istl/bcrsmatrix.hh> #include <dune/istl/bvector.hh> @@ -52,6 +51,7 @@ #include <dune/tectonic/globalruinanonlinearity.hh> #include <dune/tectonic/myconvexproblem.hh> #include <dune/tectonic/myblockproblem.hh> +#include <dune/tectonic/myvtkwriter.hh> int const dim = 2; @@ -371,7 +371,7 @@ int main(int argc, char *argv[]) { FunctionalAssembler<P0Basis>(p0Basis) .assemble(localStressAssembler, vonMisesStress, true); - Dune::VTKWriter<GridView> writer(leafView); + Dune::MyVTKWriter<GridView> writer(leafView); std::string filename((boost::format("obs%d") % run).str()); // Note: These pointers cannot be const.