1 // -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 // vi: set et ts=4 sw=2 sts=2:
3 #ifndef DUNE_PRINTGRID_HH
4 #define DUNE_PRINTGRID_HH
5 
6 #include <fstream>
7 #include <string>
8 
9 #include <dune/common/exceptions.hh>
10 #include <dune/common/parallel/mpihelper.hh>
11 #include <dune/grid/common/mcmgmapper.hh>
12 
13 namespace Dune {
14 
15   namespace {
16 
17     template<int dim>
18     struct ElementDataLayout
19     {
containsDune::__anone96389650111::ElementDataLayout20       bool contains (Dune::GeometryType gt)
21       {
22         return gt.dim()==dim;
23       }
24     };
25 
26     template<int dim>
27     struct NodeDataLayout
28     {
containsDune::__anone96389650111::NodeDataLayout29       bool contains (Dune::GeometryType gt)
30       {
31         return gt.dim()==0;
32       }
33     };
34 
35     // Move a point closer to basegeo's center by factor scale (used for drawing relative to the element)
36     template <typename B, typename C>
centrify(const B & basegeo,const C & coords,const double scale)37     C centrify (const B& basegeo, const C& coords, const double scale) {
38       C ret = coords;
39       ret -= basegeo.center();
40       ret *= scale;
41       ret += basegeo.center();
42       return ret;
43     }
44 
45     // Add a line to the plotfile from p1 to p2
46     template <typename Coord>
draw_line(std::ofstream & plotfile,const Coord & p1,const Coord & p2,std::string options)47     void draw_line (std::ofstream &plotfile, const Coord &p1, const Coord &p2, std::string options) {
48       plotfile << "set object poly from ";
49       plotfile << p1[0] << "," << p1[1] << " to ";
50       plotfile << p2[0] << "," << p2[1] << " to ";
51       plotfile << p1[0] << "," << p1[1];
52       plotfile << " " << options << std::endl;
53     }
54 
55   }
56 
57   /** \brief Print a grid as a gnuplot for testing and development
58    *  \tparam GridType the type of grid to work with
59    *  \param grid the grid to print
60    *  \param helper an MPIHelper to create unique output file names in parallel case
61    *  \param output_file the base of the output filename
62    *  \param size size of the plot in pixels; increase if plot is too cramped
63    *  \param execute_plot whether to execute gnuplot automatically
64    *  \param png whether to use PNG or SVG as the output format
65    *  \param local_corner_indices whether to show local corner indices
66    *  \param local_intersection_indices whether to show local intersection indices
67    *  \param outer_normals whether to show outer normals of intersections
68    *  Creates a gnuplot (one per process if parallel) showing the grid structure with indices, intersection types etc.
69    */
70   template <typename GridType>
printGrid(const GridType & grid,const Dune::MPIHelper & helper,std::string output_file="printgrid",int size=2000,bool execute_plot=true,bool png=true,bool local_corner_indices=true,bool local_intersection_indices=true,bool outer_normals=true)71   void printGrid (const GridType& grid, const Dune::MPIHelper& helper, std::string output_file = "printgrid",
72                   int size = 2000, bool execute_plot = true, bool png = true, bool local_corner_indices = true,
73                   bool local_intersection_indices = true, bool outer_normals = true)
74   {
75 
76     // Create output file
77     output_file = output_file + "_" + std::to_string(helper.rank());
78     std::string plot_file_name = output_file + ".gnuplot";
79     std::ofstream plotfile (plot_file_name, std::ios::out | std::ios::trunc);
80     if (!plotfile.is_open()) {
81       DUNE_THROW(Dune::IOError, "Could not create plot file " << output_file << "!");
82       return;
83     }
84 
85     // Basic plot settings
86     plotfile << "set size ratio -1" << std::endl;
87     if (png) {
88       plotfile << "set terminal png size " << size << "," << size << std::endl;
89       plotfile << "set output '" << output_file << ".png'" << std::endl;
90     } else {
91       plotfile << "set terminal svg size " << size << "," << size << " enhanced background rgb 'white'" << std::endl;
92       plotfile << "set output '" << output_file << ".svg'" << std::endl;
93     }
94 
95     // Get GridView
96     typedef typename GridType::LeafGridView GV;
97     const GV gv = grid.leafGridView();
98 
99     // Create mappers used to retrieve indices
100     typedef typename Dune::LeafMultipleCodimMultipleGeomTypeMapper<GridType> Mapper;
101     const Mapper elementmapper(grid, mcmgElementLayout());
102     const Mapper nodemapper(grid, mcmgVertexLayout());
103 
104     // Create iterators
105     typedef typename GV::template Codim<0 >::Iterator LeafIterator;
106     typedef typename GV::IntersectionIterator IntersectionIterator;
107 
108     LeafIterator it = gv.template begin<0>();
109 
110     // Will contain min/max coordinates. Needed for scaling of the plot
111     Dune::FieldVector<typename GridType::ctype,2> max_coord (it->geometry().center()), min_coord (max_coord);
112 
113     // Iterate over elements
114     for (; it != gv.template end<0>(); ++it) {
115 
116       const auto& entity = *it;
117       auto geo = entity.geometry();
118 
119       // Plot element index
120       int element_id = elementmapper.index(entity);
121       plotfile << "set label at " << geo.center()[0] << "," << geo.center()[1] << " '"
122             << element_id << "' center" << std::endl;
123 
124       for (int i = 0; i < geo.corners(); ++i) {
125         // Plot corner indices
126         const int globalNodeNumber1 = nodemapper.subIndex(entity, i, 2);
127         auto labelpos = centrify (geo, geo.corner(i), 0.7);
128         plotfile << "set label at " << labelpos[0] << "," << labelpos[1] << " '" << globalNodeNumber1;
129         if (local_corner_indices)
130           plotfile << "(" << i << ")";
131         plotfile << "' center" << std::endl;
132 
133         // Adapt min / max coordinates
134         for (int dim = 0; dim < 2; ++dim) {
135           if (geo.corner(i)[dim] < min_coord[dim])
136             min_coord[dim] = geo.corner(i)[dim];
137           else if (geo.corner(i)[dim] > max_coord[dim])
138             max_coord[dim] = geo.corner(i)[dim];
139         }
140       }
141 
142       // Iterate over intersections
143       for (IntersectionIterator is = gv.ibegin(entity); is != gv.iend(entity); ++is) {
144 
145         const auto& intersection = *is;
146         auto igeo = intersection.geometry();
147 
148         // Draw intersection line
149         draw_line (plotfile, igeo.corner(0), igeo.corner(1), "fs empty border 1");
150 
151         // Plot local intersection index
152         if (local_intersection_indices) {
153           auto label_pos = centrify (geo, igeo.center(), 0.8);
154           plotfile << "set label at " << label_pos[0] << "," << label_pos[1]
155                 << " '" << intersection.indexInInside() << "' center" << std::endl;
156         }
157 
158         // Plot outer normal
159         if (outer_normals) {
160           auto intersection_pos = igeo.center();
161           auto normal = intersection.centerUnitOuterNormal();
162           normal *= 0.15 * igeo.volume();
163           auto normal_end = intersection_pos + normal;
164           plotfile << "set arrow from " << intersection_pos[0] << "," << intersection_pos[1]
165                 << " to " << normal_end[0] << "," << normal_end[1] << " lt rgb \"gray\"" << std::endl;
166         }
167 
168         // Get corners for inner intersection representation
169         auto inner_corner1 = centrify (geo, igeo.corner(0), 0.5);
170         auto inner_corner2 = centrify (geo, igeo.corner(1), 0.5);
171 
172         // Thick line in case of boundary()
173         if (intersection.boundary())
174           draw_line (plotfile, inner_corner1, inner_corner2, "fs empty border 3 lw 4");
175 
176         // Thin line with color according to neighbor()
177         if (intersection.neighbor())
178           draw_line (plotfile, inner_corner1, inner_corner2, "fs empty border 2");
179         else
180           draw_line (plotfile, inner_corner1, inner_corner2, "fs empty border 1");
181       }
182 
183     }
184 
185     // Finish plot, pass extend of the grid
186     Dune::FieldVector<typename GridType::ctype,2> extend (max_coord - min_coord);
187 
188     extend *= 0.2;
189     min_coord -= extend;
190     max_coord += extend;
191     plotfile << "plot [" << min_coord[0] << ":" << max_coord[0] << "] [" << min_coord[1]
192           << ":" << max_coord[1] << "] NaN notitle" << std::endl;
193     plotfile.close();
194 
195     if (execute_plot) {
196       std::string cmd = "gnuplot -p '" + plot_file_name + "'";
197       if (std::system (cmd.c_str()) != 0)
198         DUNE_THROW(Dune::Exception,"Error running GNUPlot: " << cmd);
199     }
200   }
201 
202 }
203 
204 #endif // #ifndef DUNE_PRINTGRID_HH
205