Reference documentation for deal.II version 9.4.0
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
data_out_base.cc
Go to the documentation of this file.
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 1999 - 2022 by the deal.II authors
4 //
5 // This file is part of the deal.II library.
6 //
7 // The deal.II library is free software; you can use it, redistribute
8 // it, and/or modify it under the terms of the GNU Lesser General
9 // Public License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 // The full text of the license can be found in the file LICENSE.md at
12 // the top level directory of deal.II.
13 //
14 // ---------------------------------------------------------------------
15 
16 
17 // TODO: Do neighbors for dx and povray smooth triangles
18 
19 //--------------------------------------------------------------------
20 // Remarks on the implementations
21 //
22 // Variable names: in most functions, variable names have been
23 // standardized in the following way:
24 //
25 // n1, n2, ni Number of points in coordinate direction 1, 2, i
26 // will be 1 if i>=dim
27 //
28 // i1, i2, ii Loop variable running up to ni
29 //
30 // d1, d2, di Multiplicators for ii to find positions in the
31 // array of nodes.
32 //--------------------------------------------------------------------
33 
36 #include <deal.II/base/mpi.h>
40 #include <deal.II/base/utilities.h>
41 
43 
44 #include <algorithm>
45 #include <cmath>
46 #include <cstring>
47 #include <ctime>
48 #include <fstream>
49 #include <iomanip>
50 #include <memory>
51 #include <set>
52 #include <sstream>
53 
54 // we use std::uint32_t and std::uint8_t below, which are declared here:
55 #include <cstdint>
56 #include <vector>
57 
58 #ifdef DEAL_II_WITH_ZLIB
59 # include <zlib.h>
60 #endif
61 
62 #ifdef DEAL_II_WITH_HDF5
63 # include <hdf5.h>
64 #endif
65 
67 
68 
69 // we need the following exception from a global function, so can't declare it
70 // in the usual way inside a class
71 namespace
72 {
73  DeclException2(ExcUnexpectedInput,
74  std::string,
75  std::string,
76  << "Unexpected input: expected line\n <" << arg1
77  << ">\nbut got\n <" << arg2 << ">");
78 }
79 
80 
81 namespace
82 {
83 #ifdef DEAL_II_WITH_ZLIB
88  int
89  get_zlib_compression_level(
91  {
92  switch (level)
93  {
95  return Z_NO_COMPRESSION;
97  return Z_BEST_SPEED;
99  return Z_BEST_COMPRESSION;
101  return Z_DEFAULT_COMPRESSION;
102  default:
103  Assert(false, ExcNotImplemented());
104  return Z_NO_COMPRESSION;
105  }
106  }
107 
112  template <typename T>
113  void
114  write_compressed_block(const std::vector<T> & data,
115  const DataOutBase::VtkFlags &flags,
116  std::ostream & output_stream)
117  {
118  if (data.size() != 0)
119  {
120  const std::size_t uncompressed_size = (data.size() * sizeof(T));
121 
122  // While zlib's compress2 uses unsigned long (which is 64bits
123  // on Linux), the vtu compression header stores the block size
124  // as an std::uint32_t (see below). While we could implement
125  // writing several smaller blocks, we haven't done that. Let's
126  // trigger an error for the user instead:
127  AssertThrow(uncompressed_size <=
130 
131  // allocate a buffer for compressing data and do so
132  auto compressed_data_length = compressBound(uncompressed_size);
133  AssertThrow(compressed_data_length <=
136 
137  std::vector<unsigned char> compressed_data(compressed_data_length);
138 
139  int err =
140  compress2(&compressed_data[0],
141  &compressed_data_length,
142  reinterpret_cast<const Bytef *>(data.data()),
143  uncompressed_size,
144  get_zlib_compression_level(flags.compression_level));
145  (void)err;
146  Assert(err == Z_OK, ExcInternalError());
147 
148  // Discard the unnecessary bytes
149  compressed_data.resize(compressed_data_length);
150 
151  // now encode the compression header
152  const std::uint32_t compression_header[4] = {
153  1, /* number of blocks */
154  static_cast<std::uint32_t>(uncompressed_size), /* size of block */
155  static_cast<std::uint32_t>(
156  uncompressed_size), /* size of last block */
157  static_cast<std::uint32_t>(
158  compressed_data_length)}; /* list of compressed sizes of blocks */
159 
160  const auto header_start =
161  reinterpret_cast<const unsigned char *>(&compression_header[0]);
162 
163  output_stream << Utilities::encode_base64(
164  {header_start,
165  header_start + 4 * sizeof(std::uint32_t)})
166  << Utilities::encode_base64(compressed_data);
167  }
168  }
169 #endif
170 } // namespace
171 
172 
173 // some declarations of functions and locally used classes
174 namespace DataOutBase
175 {
176  namespace
177  {
183  class SvgCell
184  {
185  public:
186  // Center of the cell (three-dimensional)
188 
193 
198  float depth;
199 
204 
205  // Center of the cell (projected, two-dimensional)
207 
211  bool
212  operator<(const SvgCell &) const;
213  };
214 
215  bool
216  SvgCell::operator<(const SvgCell &e) const
217  {
218  // note the "wrong" order in which we sort the elements
219  return depth > e.depth;
220  }
221 
222 
223 
229  class EpsCell2d
230  {
231  public:
235  Point<2> vertices[4];
236 
241  float color_value;
242 
247  float depth;
248 
252  bool
253  operator<(const EpsCell2d &) const;
254  };
255 
256  bool
257  EpsCell2d::operator<(const EpsCell2d &e) const
258  {
259  // note the "wrong" order in which we sort the elements
260  return depth > e.depth;
261  }
262 
263 
264 
275  template <int dim, int spacedim, typename Number = double>
276  void
277  write_gmv_reorder_data_vectors(
278  const std::vector<Patch<dim, spacedim>> &patches,
279  Table<2, Number> & data_vectors)
280  {
281  // If there is nothing to write, just return
282  if (patches.size() == 0)
283  return;
284 
285  // unlike in the main function, we don't have here the data_names field,
286  // so we initialize it with the number of data sets in the first patch.
287  // the equivalence of these two definitions is checked in the main
288  // function.
289 
290  // we have to take care, however, whether the points are appended to the
291  // end of the patch.data table
292  const unsigned int n_data_sets = patches[0].points_are_available ?
293  (patches[0].data.n_rows() - spacedim) :
294  patches[0].data.n_rows();
295 
296  Assert(data_vectors.size()[0] == n_data_sets, ExcInternalError());
297 
298  // loop over all patches
299  unsigned int next_value = 0;
300  for (const auto &patch : patches)
301  {
302  const unsigned int n_subdivisions = patch.n_subdivisions;
303  (void)n_subdivisions;
304 
305  Assert((patch.data.n_rows() == n_data_sets &&
306  !patch.points_are_available) ||
307  (patch.data.n_rows() == n_data_sets + spacedim &&
308  patch.points_are_available),
309  ExcDimensionMismatch(patch.points_are_available ?
310  (n_data_sets + spacedim) :
311  n_data_sets,
312  patch.data.n_rows()));
313  Assert(patch.reference_cell != ReferenceCells::get_hypercube<dim>() ||
314  (n_data_sets == 0) ||
315  (patch.data.n_cols() ==
316  Utilities::fixed_power<dim>(n_subdivisions + 1)),
317  ExcInvalidDatasetSize(patch.data.n_cols(),
318  n_subdivisions + 1));
319 
320  for (unsigned int i = 0; i < patch.data.n_cols(); ++i, ++next_value)
321  for (unsigned int data_set = 0; data_set < n_data_sets; ++data_set)
322  data_vectors[data_set][next_value] = patch.data(data_set, i);
323  }
324 
325  for (unsigned int data_set = 0; data_set < n_data_sets; ++data_set)
326  Assert(data_vectors[data_set].size() == next_value, ExcInternalError());
327  }
328  } // namespace
329 
330 
331 
333  : flags(false, true)
334  , node_dim(numbers::invalid_unsigned_int)
335  , num_cells(numbers::invalid_unsigned_int)
336  {}
337 
338 
339 
341  : flags(flags)
342  , node_dim(numbers::invalid_unsigned_int)
343  , num_cells(numbers::invalid_unsigned_int)
344  {}
345 
346 
347 
348  template <int dim>
349  void
350  DataOutFilter::write_point(const unsigned int index, const Point<dim> &p)
351  {
352  node_dim = dim;
353 
354  Point<3> int_pt;
355  for (unsigned int d = 0; d < dim; ++d)
356  int_pt(d) = p(d);
357 
358  const Map3DPoint::const_iterator it = existing_points.find(int_pt);
359  unsigned int internal_ind;
360 
361  // If the point isn't in the set, or we're not filtering duplicate points,
362  // add it
363  if (it == existing_points.end() || !flags.filter_duplicate_vertices)
364  {
365  internal_ind = existing_points.size();
366  existing_points.insert(std::make_pair(int_pt, internal_ind));
367  }
368  else
369  {
370  internal_ind = it->second;
371  }
372  // Now add the index to the list of filtered points
373  filtered_points[index] = internal_ind;
374  }
375 
376 
377 
378  void
380  const unsigned int pt_index)
381  {
383 
384  // (Re)-initialize counter at any first call to this method.
385  if (cell_index == 0)
386  num_cells = 1;
387  }
388 
389 
390 
391  void
392  DataOutFilter::fill_node_data(std::vector<double> &node_data) const
393  {
394  node_data.resize(existing_points.size() * node_dim);
395 
396  for (const auto &existing_point : existing_points)
397  {
398  for (unsigned int d = 0; d < node_dim; ++d)
399  node_data[node_dim * existing_point.second + d] =
400  existing_point.first(d);
401  }
402  }
403 
404 
405 
406  void
407  DataOutFilter::fill_cell_data(const unsigned int local_node_offset,
408  std::vector<unsigned int> &cell_data) const
409  {
410  cell_data.resize(filtered_cells.size());
411 
412  for (const auto &filtered_cell : filtered_cells)
413  {
414  cell_data[filtered_cell.first] =
415  filtered_cell.second + local_node_offset;
416  }
417  }
418 
419 
420 
421  std::string
422  DataOutFilter::get_data_set_name(const unsigned int set_num) const
423  {
424  return data_set_names.at(set_num);
425  }
426 
427 
428 
429  unsigned int
430  DataOutFilter::get_data_set_dim(const unsigned int set_num) const
431  {
432  return data_set_dims.at(set_num);
433  }
434 
435 
436 
437  const double *
438  DataOutFilter::get_data_set(const unsigned int set_num) const
439  {
440  return data_sets[set_num].data();
441  }
442 
443 
444 
445  unsigned int
447  {
448  return existing_points.size();
449  }
450 
451 
452 
453  unsigned int
455  {
456  return num_cells;
457  }
458 
459 
460 
461  unsigned int
463  {
464  return data_set_names.size();
465  }
466 
467 
468 
469  void
471  {}
472 
473 
474 
475  void
477  {}
478 
479 
480 
481  template <int dim>
482  void
483  DataOutFilter::write_cell(const unsigned int index,
484  const unsigned int start,
485  const unsigned int d1,
486  const unsigned int d2,
487  const unsigned int d3)
488  {
489  ++num_cells;
490 
491  const unsigned int base_entry =
493 
494  internal_add_cell(base_entry + 0, start);
495  if (dim >= 1)
496  {
497  internal_add_cell(base_entry + 1, start + d1);
498  if (dim >= 2)
499  {
500  internal_add_cell(base_entry + 2, start + d2 + d1);
501  internal_add_cell(base_entry + 3, start + d2);
502  if (dim >= 3)
503  {
504  internal_add_cell(base_entry + 4, start + d3);
505  internal_add_cell(base_entry + 5, start + d3 + d1);
506  internal_add_cell(base_entry + 6, start + d3 + d2 + d1);
507  internal_add_cell(base_entry + 7, start + d3 + d2);
508  }
509  }
510  }
511  }
512 
513 
514 
515  void
516  DataOutFilter::write_cell_single(const unsigned int index,
517  const unsigned int start,
518  const unsigned int n_points,
520  {
521  ++num_cells;
522 
523  const unsigned int base_entry = index * n_points;
524 
525  static const std::array<unsigned int, 5> table = {{0, 1, 3, 2, 4}};
526 
527  for (unsigned int i = 0; i < n_points; ++i)
528  internal_add_cell(base_entry + i,
530  table[i] :
531  i));
532  }
533 
534 
535 
536  void
537  DataOutFilter::write_data_set(const std::string & name,
538  const unsigned int dimension,
539  const unsigned int set_num,
540  const Table<2, double> &data_vectors)
541  {
542  unsigned int new_dim;
543 
544  // HDF5/XDMF output only supports 1D or 3D output, so force rearrangement if
545  // needed
546  if (flags.xdmf_hdf5_output && dimension != 1)
547  new_dim = 3;
548  else
549  new_dim = dimension;
550 
551  // Record the data set name, dimension, and allocate space for it
552  data_set_names.push_back(name);
553  data_set_dims.push_back(new_dim);
554  data_sets.emplace_back(new_dim * existing_points.size());
555 
556  // TODO: averaging, min/max, etc for merged vertices
557  for (unsigned int i = 0; i < filtered_points.size(); ++i)
558  {
559  const unsigned int r = filtered_points[i];
560 
561  for (unsigned int d = 0; d < new_dim; ++d)
562  {
563  if (d < dimension)
564  data_sets.back()[r * new_dim + d] = data_vectors(set_num + d, i);
565  else
566  data_sets.back()[r * new_dim + d] = 0;
567  }
568  }
569  }
570 } // namespace DataOutBase
571 
572 
573 
574 //----------------------------------------------------------------------//
575 // Auxiliary data
576 //----------------------------------------------------------------------//
577 
578 namespace
579 {
580  const char *gmv_cell_type[4] = {"", "line 2", "quad 4", "hex 8"};
581 
582  const char *ucd_cell_type[4] = {"pt", "line", "quad", "hex"};
583 
584  const char *tecplot_cell_type[4] = {"", "lineseg", "quadrilateral", "brick"};
585 
586 #ifdef DEAL_II_HAVE_TECPLOT
587  const unsigned int tecplot_binary_cell_type[4] = {0, 0, 1, 3};
588 #endif
589 
590  // Define cell id using VTK nomenclature for linear, quadratic and
591  // high-order Lagrange cells
592  enum vtk_linear_cell_type
593  {
594  VTK_VERTEX = 1,
595  VTK_LINE = 3,
596  VTK_TRIANGLE = 5,
597  VTK_QUAD = 9,
598  VTK_TETRA = 10,
599  VTK_HEXAHEDRON = 12,
600  VTK_WEDGE = 13,
601  VTK_PYRAMID = 14
602  };
603 
604  enum vtk_quadratic_cell_type
605  {
606  VTK_QUADRATIC_EDGE = 21,
607  VTK_QUADRATIC_TRIANGLE = 22,
608  VTK_QUADRATIC_QUAD = 23,
609  VTK_QUADRATIC_TETRA = 24,
610  VTK_QUADRATIC_HEXAHEDRON = 25,
611  VTK_QUADRATIC_WEDGE = 26,
612  VTK_QUADRATIC_PYRAMID = 27
613  };
614 
615  enum vtk_lagrange_cell_type
616  {
617  VTK_LAGRANGE_CURVE = 68,
618  VTK_LAGRANGE_TRIANGLE = 69,
619  VTK_LAGRANGE_QUADRILATERAL = 70,
620  VTK_LAGRANGE_TETRAHEDRON = 71,
621  VTK_LAGRANGE_HEXAHEDRON = 72,
622  VTK_LAGRANGE_WEDGE = 73,
623  VTK_LAGRANGE_PYRAMID = 74
624  };
625 
630  template <int dim, int spacedim>
631  std::array<unsigned int, 3>
632  extract_vtk_patch_info(const DataOutBase::Patch<dim, spacedim> &patch,
633  const bool write_higher_order_cells)
634  {
635  std::array<unsigned int, 3> vtk_cell_id{};
636 
637  if (write_higher_order_cells)
638  {
639  if (patch.reference_cell == ReferenceCells::get_hypercube<dim>())
640  {
641  const std::array<unsigned int, 4> cell_type_by_dim{
642  {VTK_VERTEX,
643  VTK_LAGRANGE_CURVE,
644  VTK_LAGRANGE_QUADRILATERAL,
645  VTK_LAGRANGE_HEXAHEDRON}};
646  vtk_cell_id[0] = cell_type_by_dim[dim];
647  vtk_cell_id[1] = 1;
648  }
649  else if (patch.reference_cell == ReferenceCells::Triangle)
650  {
651  vtk_cell_id[0] = VTK_LAGRANGE_TRIANGLE;
652  vtk_cell_id[1] = 1;
653  }
654  else
655  {
656  Assert(false, ExcNotImplemented());
657  }
658  }
659  else if (patch.reference_cell == ReferenceCells::Triangle &&
660  patch.data.n_cols() == 3)
661  {
662  vtk_cell_id[0] = VTK_TRIANGLE;
663  vtk_cell_id[1] = 1;
664  }
665  else if (patch.reference_cell == ReferenceCells::Triangle &&
666  patch.data.n_cols() == 6)
667  {
668  vtk_cell_id[0] = VTK_QUADRATIC_TRIANGLE;
669  vtk_cell_id[1] = 1;
670  }
671  else if (patch.reference_cell == ReferenceCells::Tetrahedron &&
672  patch.data.n_cols() == 4)
673  {
674  vtk_cell_id[0] = VTK_TETRA;
675  vtk_cell_id[1] = 1;
676  }
677  else if (patch.reference_cell == ReferenceCells::Tetrahedron &&
678  patch.data.n_cols() == 10)
679  {
680  vtk_cell_id[0] = VTK_QUADRATIC_TETRA;
681  vtk_cell_id[1] = 1;
682  }
683  else if (patch.reference_cell == ReferenceCells::Wedge &&
684  patch.data.n_cols() == 6)
685  {
686  vtk_cell_id[0] = VTK_WEDGE;
687  vtk_cell_id[1] = 1;
688  }
689  else if (patch.reference_cell == ReferenceCells::Pyramid &&
690  patch.data.n_cols() == 5)
691  {
692  vtk_cell_id[0] = VTK_PYRAMID;
693  vtk_cell_id[1] = 1;
694  }
695  else if (patch.reference_cell == ReferenceCells::get_hypercube<dim>())
696  {
697  const std::array<unsigned int, 4> cell_type_by_dim{
698  {VTK_VERTEX, VTK_LINE, VTK_QUAD, VTK_HEXAHEDRON}};
699  vtk_cell_id[0] = cell_type_by_dim[dim];
700  vtk_cell_id[1] = Utilities::pow(patch.n_subdivisions, dim);
701  }
702  else
703  {
704  Assert(false, ExcNotImplemented());
705  }
706 
707  if (patch.reference_cell != ReferenceCells::get_hypercube<dim>() ||
708  write_higher_order_cells)
709  vtk_cell_id[2] = patch.data.n_cols();
710  else
711  vtk_cell_id[2] = GeometryInfo<dim>::vertices_per_cell;
712 
713  return vtk_cell_id;
714  }
715 
716  //----------------------------------------------------------------------//
717  // Auxiliary functions
718  //----------------------------------------------------------------------//
719 
720  // For a given patch that corresponds to a hypercube cell, compute the
721  // location of a node interpolating the corner nodes linearly
722  // at the point lattice_location/n_subdivisions where lattice_location
723  // is a dim-dimensional integer vector. If the points are
724  // saved in the patch.data member, return the saved point instead.
725  template <int dim, int spacedim>
726  inline Point<spacedim>
727  get_equispaced_location(
728  const DataOutBase::Patch<dim, spacedim> & patch,
729  const std::initializer_list<unsigned int> &lattice_location,
730  const unsigned int n_subdivisions)
731  {
732  // This function only makes sense when called on hypercube cells
733  Assert(patch.reference_cell == ReferenceCells::get_hypercube<dim>(),
734  ExcInternalError());
735 
736  Assert(lattice_location.size() == dim, ExcInternalError());
737 
738  const unsigned int xstep = (dim > 0 ? *(lattice_location.begin() + 0) : 0);
739  const unsigned int ystep = (dim > 1 ? *(lattice_location.begin() + 1) : 0);
740  const unsigned int zstep = (dim > 2 ? *(lattice_location.begin() + 2) : 0);
741 
742  // If the patch stores the locations of nodes (rather than of only the
743  // vertices), then obtain the location by direct lookup.
744  if (patch.points_are_available)
745  {
746  Assert(n_subdivisions == patch.n_subdivisions, ExcNotImplemented());
747 
748  unsigned int point_no = 0;
749  switch (dim)
750  {
751  case 3:
752  AssertIndexRange(zstep, n_subdivisions + 1);
753  point_no += (n_subdivisions + 1) * (n_subdivisions + 1) * zstep;
755  case 2:
756  AssertIndexRange(ystep, n_subdivisions + 1);
757  point_no += (n_subdivisions + 1) * ystep;
759  case 1:
760  AssertIndexRange(xstep, n_subdivisions + 1);
761  point_no += xstep;
763  case 0:
764  // break here for dim<=3
765  break;
766 
767  default:
768  Assert(false, ExcNotImplemented());
769  }
770  Point<spacedim> node;
771  for (unsigned int d = 0; d < spacedim; ++d)
772  node[d] = patch.data(patch.data.size(0) - spacedim + d, point_no);
773  return node;
774  }
775  else
776  // The patch does not store node locations, so we have to interpolate
777  // between its vertices:
778  {
779  if (dim == 0)
780  return patch.vertices[0];
781  else
782  {
783  // perform a dim-linear interpolation
784  const double stepsize = 1. / n_subdivisions;
785  const double xfrac = xstep * stepsize;
786 
787  Point<spacedim> node =
788  (patch.vertices[1] * xfrac) + (patch.vertices[0] * (1 - xfrac));
789  if (dim > 1)
790  {
791  const double yfrac = ystep * stepsize;
792  node *= 1 - yfrac;
793  node += ((patch.vertices[3] * xfrac) +
794  (patch.vertices[2] * (1 - xfrac))) *
795  yfrac;
796  if (dim > 2)
797  {
798  const double zfrac = zstep * stepsize;
799  node *= (1 - zfrac);
800  node += (((patch.vertices[5] * xfrac) +
801  (patch.vertices[4] * (1 - xfrac))) *
802  (1 - yfrac) +
803  ((patch.vertices[7] * xfrac) +
804  (patch.vertices[6] * (1 - xfrac))) *
805  yfrac) *
806  zfrac;
807  }
808  }
809  return node;
810  }
811  }
812  }
813 
814  // For a given patch, compute the nodes for arbitrary (non-hypercube) cells.
815  // If the points are saved in the patch.data member, return the saved point
816  // instead.
817  template <int dim, int spacedim>
818  inline Point<spacedim>
819  get_node_location(const DataOutBase::Patch<dim, spacedim> &patch,
820  const unsigned int node_index)
821  {
822  // Due to a historical accident, we are using a different indexing
823  // for pyramids in this file than we do where we create patches.
824  // So translate if necessary.
825  unsigned int point_no_actual = node_index;
827  {
829 
830  static const std::array<unsigned int, 5> table = {{0, 1, 3, 2, 4}};
831  point_no_actual = table[node_index];
832  }
833 
834  // If the patch stores the locations of nodes (rather than of only the
835  // vertices), then obtain the location by direct lookup.
836  if (patch.points_are_available)
837  {
838  Point<spacedim> node;
839  for (unsigned int d = 0; d < spacedim; ++d)
840  node[d] =
841  patch.data(patch.data.size(0) - spacedim + d, point_no_actual);
842  return node;
843  }
844  else
845  // The patch does not store node locations, so we have to interpolate
846  // between its vertices. This isn't currently implemented for anything
847  // other than one subdivision, but would go here.
848  //
849  // For n_subdivisions==1, the locations are simply those of vertices, so
850  // get the information from there.
851  {
853 
854  return patch.vertices[point_no_actual];
855  }
856  }
857 
858 
859 
867  int
868  vtk_point_index_from_ijk(const unsigned i,
869  const unsigned j,
870  const unsigned,
871  const std::array<unsigned, 2> &order)
872  {
873  const bool ibdy = (i == 0 || i == order[0]);
874  const bool jbdy = (j == 0 || j == order[1]);
875  // How many boundaries do we lie on at once?
876  const int nbdy = (ibdy ? 1 : 0) + (jbdy ? 1 : 0);
877 
878  if (nbdy == 2) // Vertex DOF
879  { // ijk is a corner node. Return the proper index (somewhere in [0,3]):
880  return (i != 0u ? (j != 0u ? 2 : 1) : (j != 0u ? 3 : 0));
881  }
882 
883  int offset = 4;
884  if (nbdy == 1) // Edge DOF
885  {
886  if (!ibdy)
887  { // On i axis
888  return (i - 1) + (j != 0u ? order[0] - 1 + order[1] - 1 : 0) +
889  offset;
890  }
891 
892  if (!jbdy)
893  { // On j axis
894  return (j - 1) +
895  (i != 0u ? order[0] - 1 :
896  2 * (order[0] - 1) + order[1] - 1) +
897  offset;
898  }
899  }
900 
901  offset += 2 * (order[0] - 1 + order[1] - 1);
902  // nbdy == 0: Face DOF
903  return offset + (i - 1) + (order[0] - 1) * ((j - 1));
904  }
905 
906 
907 
915  int
916  vtk_point_index_from_ijk(const unsigned i,
917  const unsigned j,
918  const unsigned k,
919  const std::array<unsigned, 3> &order)
920  {
921  const bool ibdy = (i == 0 || i == order[0]);
922  const bool jbdy = (j == 0 || j == order[1]);
923  const bool kbdy = (k == 0 || k == order[2]);
924  // How many boundaries do we lie on at once?
925  const int nbdy = (ibdy ? 1 : 0) + (jbdy ? 1 : 0) + (kbdy ? 1 : 0);
926 
927  if (nbdy == 3) // Vertex DOF
928  { // ijk is a corner node. Return the proper index (somewhere in [0,7]):
929  return (i != 0u ? (j != 0u ? 2 : 1) : (j != 0u ? 3 : 0)) +
930  (k != 0u ? 4 : 0);
931  }
932 
933  int offset = 8;
934  if (nbdy == 2) // Edge DOF
935  {
936  if (!ibdy)
937  { // On i axis
938  return (i - 1) + (j != 0u ? order[0] - 1 + order[1] - 1 : 0) +
939  (k != 0u ? 2 * (order[0] - 1 + order[1] - 1) : 0) + offset;
940  }
941  if (!jbdy)
942  { // On j axis
943  return (j - 1) +
944  (i != 0u ? order[0] - 1 :
945  2 * (order[0] - 1) + order[1] - 1) +
946  (k != 0u ? 2 * (order[0] - 1 + order[1] - 1) : 0) + offset;
947  }
948  // !kbdy, On k axis
949  offset += 4 * (order[0] - 1) + 4 * (order[1] - 1);
950  return (k - 1) +
951  (order[2] - 1) *
952  (i != 0u ? (j != 0u ? 3 : 1) : (j != 0u ? 2 : 0)) +
953  offset;
954  }
955 
956  offset += 4 * (order[0] - 1 + order[1] - 1 + order[2] - 1);
957  if (nbdy == 1) // Face DOF
958  {
959  if (ibdy) // On i-normal face
960  {
961  return (j - 1) + ((order[1] - 1) * (k - 1)) +
962  (i != 0u ? (order[1] - 1) * (order[2] - 1) : 0) + offset;
963  }
964  offset += 2 * (order[1] - 1) * (order[2] - 1);
965  if (jbdy) // On j-normal face
966  {
967  return (i - 1) + ((order[0] - 1) * (k - 1)) +
968  (j != 0u ? (order[2] - 1) * (order[0] - 1) : 0) + offset;
969  }
970  offset += 2 * (order[2] - 1) * (order[0] - 1);
971  // kbdy, On k-normal face
972  return (i - 1) + ((order[0] - 1) * (j - 1)) +
973  (k != 0u ? (order[0] - 1) * (order[1] - 1) : 0) + offset;
974  }
975 
976  // nbdy == 0: Body DOF
977  offset +=
978  2 * ((order[1] - 1) * (order[2] - 1) + (order[2] - 1) * (order[0] - 1) +
979  (order[0] - 1) * (order[1] - 1));
980  return offset + (i - 1) +
981  (order[0] - 1) * ((j - 1) + (order[1] - 1) * ((k - 1)));
982  }
983 
984 
985 
986  int
987  vtk_point_index_from_ijk(const unsigned,
988  const unsigned,
989  const unsigned,
990  const std::array<unsigned, 0> &)
991  {
992  Assert(false, ExcNotImplemented());
993  return 0;
994  }
995 
996 
997 
998  int
999  vtk_point_index_from_ijk(const unsigned,
1000  const unsigned,
1001  const unsigned,
1002  const std::array<unsigned, 1> &)
1003  {
1004  Assert(false, ExcNotImplemented());
1005  return 0;
1006  }
1007 
1008 
1009 
1010  template <int dim, int spacedim>
1011  static void
1012  compute_sizes(const std::vector<DataOutBase::Patch<dim, spacedim>> &patches,
1013  unsigned int & n_nodes,
1014  unsigned int & n_cells)
1015  {
1016  n_nodes = 0;
1017  n_cells = 0;
1018  for (const auto &patch : patches)
1019  {
1021  ExcMessage(
1022  "The reference cell for this patch is set to 'Invalid', "
1023  "but that is clearly not a valid choice. Did you forget "
1024  "to set the reference cell for the patch?"));
1025 
1026  // The following formula doesn't hold for non-tensor products.
1027  if (patch.reference_cell == ReferenceCells::get_hypercube<dim>())
1028  {
1029  n_nodes += Utilities::fixed_power<dim>(patch.n_subdivisions + 1);
1030  n_cells += Utilities::fixed_power<dim>(patch.n_subdivisions);
1031  }
1032  else
1033  {
1034  Assert(patch.n_subdivisions == 1, ExcNotImplemented());
1035  n_nodes += patch.reference_cell.n_vertices();
1036  n_cells += 1;
1037  }
1038  }
1039  }
1040 
1041 
1042 
1043  template <int dim, int spacedim>
1044  static void
1045  compute_sizes(const std::vector<DataOutBase::Patch<dim, spacedim>> &patches,
1046  const bool write_higher_order_cells,
1047  unsigned int &n_nodes,
1048  unsigned int &n_cells,
1049  unsigned int &n_points_and_n_cells)
1050  {
1051  n_nodes = 0;
1052  n_cells = 0;
1053  n_points_and_n_cells = 0;
1054 
1055  for (const auto &patch : patches)
1056  {
1057  // The following formulas don't hold for non-tensor products.
1058  if (patch.reference_cell == ReferenceCells::get_hypercube<dim>())
1059  {
1060  n_nodes += Utilities::fixed_power<dim>(patch.n_subdivisions + 1);
1061 
1062  if (write_higher_order_cells)
1063  {
1064  n_cells += 1;
1065  n_points_and_n_cells +=
1066  1 + Utilities::fixed_power<dim>(patch.n_subdivisions + 1);
1067  }
1068  else
1069  {
1070  n_cells += Utilities::fixed_power<dim>(patch.n_subdivisions);
1071  n_points_and_n_cells +=
1072  Utilities::fixed_power<dim>(patch.n_subdivisions) *
1074  }
1075  }
1076  else
1077  {
1078  n_nodes += patch.data.n_cols();
1079  n_cells += 1;
1080  n_points_and_n_cells += patch.data.n_cols() + 1;
1081  }
1082  }
1083  }
1084 
1090  template <typename FlagsType>
1091  class StreamBase
1092  {
1093  public:
1094  /*
1095  * Constructor. Stores a reference to the output stream for immediate use.
1096  */
1097  StreamBase(std::ostream &stream, const FlagsType &flags)
1098  : selected_component(numbers::invalid_unsigned_int)
1099  , stream(stream)
1100  , flags(flags)
1101  {}
1102 
1107  template <int dim>
1108  void
1109  write_point(const unsigned int, const Point<dim> &)
1110  {
1111  Assert(false,
1112  ExcMessage("The derived class you are using needs to "
1113  "reimplement this function if you want to call "
1114  "it."));
1115  }
1116 
1122  void
1123  flush_points()
1124  {}
1125 
1131  template <int dim>
1132  void
1133  write_cell(const unsigned int /*index*/,
1134  const unsigned int /*start*/,
1135  const unsigned int /*x_offset*/,
1136  const unsigned int /*y_offset*/,
1137  const unsigned int /*z_offset*/)
1138  {
1139  Assert(false,
1140  ExcMessage("The derived class you are using needs to "
1141  "reimplement this function if you want to call "
1142  "it."));
1143  }
1144 
1151  void
1152  write_cell_single(const unsigned int index,
1153  const unsigned int start,
1154  const unsigned int n_points,
1156  {
1157  (void)index;
1158  (void)start;
1159  (void)n_points;
1160  (void)reference_cell;
1161 
1162  Assert(false,
1163  ExcMessage("The derived class you are using needs to "
1164  "reimplement this function if you want to call "
1165  "it."));
1166  }
1167 
1174  void
1175  flush_cells()
1176  {}
1177 
1182  template <typename T>
1183  std::ostream &
1184  operator<<(const T &t)
1185  {
1186  stream << t;
1187  return stream;
1188  }
1189 
1196  unsigned int selected_component;
1197 
1198  protected:
1203  std::ostream &stream;
1204 
1208  const FlagsType flags;
1209  };
1210 
1214  class DXStream : public StreamBase<DataOutBase::DXFlags>
1215  {
1216  public:
1217  DXStream(std::ostream &stream, const DataOutBase::DXFlags &flags);
1218 
1219  template <int dim>
1220  void
1221  write_point(const unsigned int index, const Point<dim> &);
1222 
1231  template <int dim>
1232  void
1233  write_cell(const unsigned int index,
1234  const unsigned int start,
1235  const unsigned int x_offset,
1236  const unsigned int y_offset,
1237  const unsigned int z_offset);
1238 
1245  template <typename data>
1246  void
1247  write_dataset(const unsigned int index, const std::vector<data> &values);
1248  };
1249 
1253  class GmvStream : public StreamBase<DataOutBase::GmvFlags>
1254  {
1255  public:
1256  GmvStream(std::ostream &stream, const DataOutBase::GmvFlags &flags);
1257 
1258  template <int dim>
1259  void
1260  write_point(const unsigned int index, const Point<dim> &);
1261 
1270  template <int dim>
1271  void
1272  write_cell(const unsigned int index,
1273  const unsigned int start,
1274  const unsigned int x_offset,
1275  const unsigned int y_offset,
1276  const unsigned int z_offset);
1277  };
1278 
1282  class TecplotStream : public StreamBase<DataOutBase::TecplotFlags>
1283  {
1284  public:
1285  TecplotStream(std::ostream &stream, const DataOutBase::TecplotFlags &flags);
1286 
1287  template <int dim>
1288  void
1289  write_point(const unsigned int index, const Point<dim> &);
1290 
1299  template <int dim>
1300  void
1301  write_cell(const unsigned int index,
1302  const unsigned int start,
1303  const unsigned int x_offset,
1304  const unsigned int y_offset,
1305  const unsigned int z_offset);
1306  };
1307 
1311  class UcdStream : public StreamBase<DataOutBase::UcdFlags>
1312  {
1313  public:
1314  UcdStream(std::ostream &stream, const DataOutBase::UcdFlags &flags);
1315 
1316  template <int dim>
1317  void
1318  write_point(const unsigned int index, const Point<dim> &);
1319 
1330  template <int dim>
1331  void
1332  write_cell(const unsigned int index,
1333  const unsigned int start,
1334  const unsigned int x_offset,
1335  const unsigned int y_offset,
1336  const unsigned int z_offset);
1337 
1344  template <typename data>
1345  void
1346  write_dataset(const unsigned int index, const std::vector<data> &values);
1347  };
1348 
1352  class VtkStream : public StreamBase<DataOutBase::VtkFlags>
1353  {
1354  public:
1355  VtkStream(std::ostream &stream, const DataOutBase::VtkFlags &flags);
1356 
1357  template <int dim>
1358  void
1359  write_point(const unsigned int index, const Point<dim> &);
1360 
1369  template <int dim>
1370  void
1371  write_cell(const unsigned int index,
1372  const unsigned int start,
1373  const unsigned int x_offset,
1374  const unsigned int y_offset,
1375  const unsigned int z_offset);
1376 
1380  void
1381  write_cell_single(const unsigned int index,
1382  const unsigned int start,
1383  const unsigned int n_points,
1384  const ReferenceCell &reference_cell);
1385 
1393  template <int dim>
1394  void
1395  write_high_order_cell(const unsigned int index,
1396  const unsigned int start,
1397  const std::vector<unsigned> &connectivity);
1398  };
1399 
1400 
1401  class VtuStream : public StreamBase<DataOutBase::VtkFlags>
1402  {
1403  public:
1404  VtuStream(std::ostream &stream, const DataOutBase::VtkFlags &flags);
1405 
1406  template <int dim>
1407  void
1408  write_point(const unsigned int index, const Point<dim> &);
1409 
1410  void
1411  flush_points();
1412 
1421  template <int dim>
1422  void
1423  write_cell(const unsigned int index,
1424  const unsigned int start,
1425  const unsigned int x_offset,
1426  const unsigned int y_offset,
1427  const unsigned int z_offset);
1428 
1432  void
1433  write_cell_single(const unsigned int index,
1434  const unsigned int start,
1435  const unsigned int n_points,
1436  const ReferenceCell &reference_cell);
1437 
1445  template <int dim>
1446  void
1447  write_high_order_cell(const unsigned int index,
1448  const unsigned int start,
1449  const std::vector<unsigned> &connectivity);
1450 
1451  void
1452  flush_cells();
1453 
1454  template <typename T>
1455  std::ostream &
1456  operator<<(const T &);
1457 
1465  template <typename T>
1466  std::ostream &
1467  operator<<(const std::vector<T> &);
1468 
1469  private:
1478  std::vector<float> vertices;
1479  std::vector<int32_t> cells;
1480  };
1481 
1482 
1483  //----------------------------------------------------------------------//
1484 
1485  DXStream::DXStream(std::ostream &out, const DataOutBase::DXFlags &f)
1486  : StreamBase<DataOutBase::DXFlags>(out, f)
1487  {}
1488 
1489 
1490  template <int dim>
1491  void
1492  DXStream::write_point(const unsigned int, const Point<dim> &p)
1493  {
1494  if (flags.coordinates_binary)
1495  {
1496  float data[dim];
1497  for (unsigned int d = 0; d < dim; ++d)
1498  data[d] = p(d);
1499  stream.write(reinterpret_cast<const char *>(data), dim * sizeof(*data));
1500  }
1501  else
1502  {
1503  for (unsigned int d = 0; d < dim; ++d)
1504  stream << p(d) << '\t';
1505  stream << '\n';
1506  }
1507  }
1508 
1509 
1510 
1511  // Separate these out to avoid an internal compiler error with intel 17
1513  {
1518  template <int dim>
1519  std::array<unsigned int, GeometryInfo<dim>::vertices_per_cell>
1520  set_node_numbers(const unsigned int /*start*/,
1521  const unsigned int /*d1*/,
1522  const unsigned int /*d2*/,
1523  const unsigned int /*d3*/)
1524  {
1525  Assert(false, ExcInternalError());
1526  return {};
1527  }
1528 
1529 
1530 
1531  template <>
1532  std::array<unsigned int, GeometryInfo<1>::vertices_per_cell>
1533  set_node_numbers<1>(const unsigned int start,
1534  const unsigned int d1,
1535  const unsigned int /*d2*/,
1536  const unsigned int /*d3*/)
1537 
1538  {
1539  std::array<unsigned int, GeometryInfo<1>::vertices_per_cell> nodes;
1540  nodes[0] = start;
1541  nodes[1] = start + d1;
1542  return nodes;
1543  }
1544 
1545 
1546 
1547  template <>
1548  std::array<unsigned int, GeometryInfo<2>::vertices_per_cell>
1549  set_node_numbers<2>(const unsigned int start,
1550  const unsigned int d1,
1551  const unsigned int d2,
1552  const unsigned int /*d3*/)
1553 
1554  {
1555  std::array<unsigned int, GeometryInfo<2>::vertices_per_cell> nodes;
1556  nodes[0] = start;
1557  nodes[1] = start + d1;
1558  nodes[2] = start + d2;
1559  nodes[3] = start + d2 + d1;
1560  return nodes;
1561  }
1562 
1563 
1564 
1565  template <>
1566  std::array<unsigned int, GeometryInfo<3>::vertices_per_cell>
1567  set_node_numbers<3>(const unsigned int start,
1568  const unsigned int d1,
1569  const unsigned int d2,
1570  const unsigned int d3)
1571  {
1572  std::array<unsigned int, GeometryInfo<3>::vertices_per_cell> nodes;
1573  nodes[0] = start;
1574  nodes[1] = start + d1;
1575  nodes[2] = start + d2;
1576  nodes[3] = start + d2 + d1;
1577  nodes[4] = start + d3;
1578  nodes[5] = start + d3 + d1;
1579  nodes[6] = start + d3 + d2;
1580  nodes[7] = start + d3 + d2 + d1;
1581  return nodes;
1582  }
1583  } // namespace DataOutBaseImplementation
1584 
1585 
1586 
1587  template <int dim>
1588  void
1589  DXStream::write_cell(unsigned int,
1590  unsigned int start,
1591  unsigned int d1,
1592  unsigned int d2,
1593  unsigned int d3)
1594  {
1595  const auto nodes =
1596  DataOutBaseImplementation::set_node_numbers<dim>(start, d1, d2, d3);
1597 
1598  if (flags.int_binary)
1599  {
1600  std::array<unsigned int, GeometryInfo<dim>::vertices_per_cell> temp;
1601  for (unsigned int i = 0; i < nodes.size(); ++i)
1602  temp[i] = nodes[GeometryInfo<dim>::dx_to_deal[i]];
1603  stream.write(reinterpret_cast<const char *>(temp.data()),
1604  temp.size() * sizeof(temp[0]));
1605  }
1606  else
1607  {
1608  for (unsigned int i = 0; i < nodes.size() - 1; ++i)
1609  stream << nodes[GeometryInfo<dim>::dx_to_deal[i]] << '\t';
1610  stream << nodes[GeometryInfo<dim>::dx_to_deal[nodes.size() - 1]]
1611  << '\n';
1612  }
1613  }
1614 
1615 
1616 
1617  template <typename data>
1618  inline void
1619  DXStream::write_dataset(const unsigned int, const std::vector<data> &values)
1620  {
1621  if (flags.data_binary)
1622  {
1623  stream.write(reinterpret_cast<const char *>(values.data()),
1624  values.size() * sizeof(data));
1625  }
1626  else
1627  {
1628  for (unsigned int i = 0; i < values.size(); ++i)
1629  stream << '\t' << values[i];
1630  stream << '\n';
1631  }
1632  }
1633 
1634 
1635 
1636  //----------------------------------------------------------------------//
1637 
1638  GmvStream::GmvStream(std::ostream &out, const DataOutBase::GmvFlags &f)
1639  : StreamBase<DataOutBase::GmvFlags>(out, f)
1640  {}
1641 
1642 
1643  template <int dim>
1644  void
1645  GmvStream::write_point(const unsigned int, const Point<dim> &p)
1646  {
1647  Assert(selected_component != numbers::invalid_unsigned_int,
1648  ExcNotInitialized());
1649  stream << p(selected_component) << ' ';
1650  }
1651 
1652 
1653 
1654  template <int dim>
1655  void
1656  GmvStream::write_cell(unsigned int,
1657  unsigned int s,
1658  unsigned int d1,
1659  unsigned int d2,
1660  unsigned int d3)
1661  {
1662  // Vertices are numbered starting with one.
1663  const unsigned int start = s + 1;
1664  stream << gmv_cell_type[dim] << '\n';
1665 
1666  stream << start;
1667  if (dim >= 1)
1668  {
1669  stream << '\t' << start + d1;
1670  if (dim >= 2)
1671  {
1672  stream << '\t' << start + d2 + d1 << '\t' << start + d2;
1673  if (dim >= 3)
1674  {
1675  stream << '\t' << start + d3 << '\t' << start + d3 + d1 << '\t'
1676  << start + d3 + d2 + d1 << '\t' << start + d3 + d2;
1677  }
1678  }
1679  }
1680  stream << '\n';
1681  }
1682 
1683 
1684 
1685  TecplotStream::TecplotStream(std::ostream & out,
1686  const DataOutBase::TecplotFlags &f)
1687  : StreamBase<DataOutBase::TecplotFlags>(out, f)
1688  {}
1689 
1690 
1691  template <int dim>
1692  void
1693  TecplotStream::write_point(const unsigned int, const Point<dim> &p)
1694  {
1695  Assert(selected_component != numbers::invalid_unsigned_int,
1696  ExcNotInitialized());
1697  stream << p(selected_component) << '\n';
1698  }
1699 
1700 
1701 
1702  template <int dim>
1703  void
1704  TecplotStream::write_cell(unsigned int,
1705  unsigned int s,
1706  unsigned int d1,
1707  unsigned int d2,
1708  unsigned int d3)
1709  {
1710  const unsigned int start = s + 1;
1711 
1712  stream << start;
1713  if (dim >= 1)
1714  {
1715  stream << '\t' << start + d1;
1716  if (dim >= 2)
1717  {
1718  stream << '\t' << start + d2 + d1 << '\t' << start + d2;
1719  if (dim >= 3)
1720  {
1721  stream << '\t' << start + d3 << '\t' << start + d3 + d1 << '\t'
1722  << start + d3 + d2 + d1 << '\t' << start + d3 + d2;
1723  }
1724  }
1725  }
1726  stream << '\n';
1727  }
1728 
1729 
1730 
1731  UcdStream::UcdStream(std::ostream &out, const DataOutBase::UcdFlags &f)
1732  : StreamBase<DataOutBase::UcdFlags>(out, f)
1733  {}
1734 
1735 
1736  template <int dim>
1737  void
1738  UcdStream::write_point(const unsigned int index, const Point<dim> &p)
1739  {
1740  stream << index + 1 << " ";
1741  // write out coordinates
1742  for (unsigned int i = 0; i < dim; ++i)
1743  stream << p(i) << ' ';
1744  // fill with zeroes
1745  for (unsigned int i = dim; i < 3; ++i)
1746  stream << "0 ";
1747  stream << '\n';
1748  }
1749 
1750 
1751 
1752  template <int dim>
1753  void
1754  UcdStream::write_cell(unsigned int index,
1755  unsigned int start,
1756  unsigned int d1,
1757  unsigned int d2,
1758  unsigned int d3)
1759  {
1760  const auto nodes =
1761  DataOutBaseImplementation::set_node_numbers<dim>(start, d1, d2, d3);
1762 
1763  // Write out all cells and remember that all indices must be shifted by one.
1764  stream << index + 1 << "\t0 " << ucd_cell_type[dim];
1765  for (unsigned int i = 0; i < nodes.size(); ++i)
1766  stream << '\t' << nodes[GeometryInfo<dim>::ucd_to_deal[i]] + 1;
1767  stream << '\n';
1768  }
1769 
1770 
1771 
1772  template <typename data>
1773  inline void
1774  UcdStream::write_dataset(const unsigned int index,
1775  const std::vector<data> &values)
1776  {
1777  stream << index + 1;
1778  for (unsigned int i = 0; i < values.size(); ++i)
1779  stream << '\t' << values[i];
1780  stream << '\n';
1781  }
1782 
1783 
1784 
1785  //----------------------------------------------------------------------//
1786 
1787  VtkStream::VtkStream(std::ostream &out, const DataOutBase::VtkFlags &f)
1788  : StreamBase<DataOutBase::VtkFlags>(out, f)
1789  {}
1790 
1791 
1792  template <int dim>
1793  void
1794  VtkStream::write_point(const unsigned int, const Point<dim> &p)
1795  {
1796  // write out coordinates
1797  stream << p;
1798  // fill with zeroes
1799  for (unsigned int i = dim; i < 3; ++i)
1800  stream << " 0";
1801  stream << '\n';
1802  }
1803 
1804 
1805 
1806  template <int dim>
1807  void
1808  VtkStream::write_cell(unsigned int,
1809  unsigned int start,
1810  unsigned int d1,
1811  unsigned int d2,
1812  unsigned int d3)
1813  {
1814  stream << GeometryInfo<dim>::vertices_per_cell << '\t' << start;
1815 
1816  if (dim >= 1)
1817  stream << '\t' << start + d1;
1818  {
1819  if (dim >= 2)
1820  {
1821  stream << '\t' << start + d2 + d1 << '\t' << start + d2;
1822  if (dim >= 3)
1823  {
1824  stream << '\t' << start + d3 << '\t' << start + d3 + d1 << '\t'
1825  << start + d3 + d2 + d1 << '\t' << start + d3 + d2;
1826  }
1827  }
1828  }
1829  stream << '\n';
1830  }
1831 
1832  void
1833  VtkStream::write_cell_single(const unsigned int index,
1834  const unsigned int start,
1835  const unsigned int n_points,
1837  {
1838  (void)index;
1839 
1840  static const std::array<unsigned int, 5> table = {{0, 1, 3, 2, 4}};
1841 
1842  stream << '\t' << n_points;
1843  for (unsigned int i = 0; i < n_points; ++i)
1844  stream << '\t'
1845  << start +
1846  (reference_cell == ReferenceCells::Pyramid ? table[i] : i);
1847  stream << '\n';
1848  }
1849 
1850  template <int dim>
1851  void
1852  VtkStream::write_high_order_cell(const unsigned int,
1853  const unsigned int start,
1854  const std::vector<unsigned> &connectivity)
1855  {
1856  stream << connectivity.size();
1857  for (const auto &c : connectivity)
1858  stream << '\t' << start + c;
1859  stream << '\n';
1860  }
1861 
1862  VtuStream::VtuStream(std::ostream &out, const DataOutBase::VtkFlags &f)
1863  : StreamBase<DataOutBase::VtkFlags>(out, f)
1864  {}
1865 
1866 
1867  template <int dim>
1868  void
1869  VtuStream::write_point(const unsigned int, const Point<dim> &p)
1870  {
1871 #if !defined(DEAL_II_WITH_ZLIB)
1872  // write out coordinates
1873  stream << p;
1874  // fill with zeroes
1875  for (unsigned int i = dim; i < 3; ++i)
1876  stream << " 0";
1877  stream << '\n';
1878 #else
1879  // if we want to compress, then first collect all the data in an array
1880  for (unsigned int i = 0; i < dim; ++i)
1881  vertices.push_back(p[i]);
1882  for (unsigned int i = dim; i < 3; ++i)
1883  vertices.push_back(0);
1884 #endif
1885  }
1886 
1887 
1888  void
1889  VtuStream::flush_points()
1890  {
1891 #ifdef DEAL_II_WITH_ZLIB
1892  // compress the data we have in memory and write them to the stream. then
1893  // release the data
1894  *this << vertices << '\n';
1895  vertices.clear();
1896 #endif
1897  }
1898 
1899 
1900  template <int dim>
1901  void
1902  VtuStream::write_cell(unsigned int,
1903  unsigned int start,
1904  unsigned int d1,
1905  unsigned int d2,
1906  unsigned int d3)
1907  {
1908 #if !defined(DEAL_II_WITH_ZLIB)
1909  stream << start;
1910  if (dim >= 1)
1911  {
1912  stream << '\t' << start + d1;
1913  if (dim >= 2)
1914  {
1915  stream << '\t' << start + d2 + d1 << '\t' << start + d2;
1916  if (dim >= 3)
1917  {
1918  stream << '\t' << start + d3 << '\t' << start + d3 + d1 << '\t'
1919  << start + d3 + d2 + d1 << '\t' << start + d3 + d2;
1920  }
1921  }
1922  }
1923  stream << '\n';
1924 #else
1925  cells.push_back(start);
1926  if (dim >= 1)
1927  {
1928  cells.push_back(start + d1);
1929  if (dim >= 2)
1930  {
1931  cells.push_back(start + d2 + d1);
1932  cells.push_back(start + d2);
1933  if (dim >= 3)
1934  {
1935  cells.push_back(start + d3);
1936  cells.push_back(start + d3 + d1);
1937  cells.push_back(start + d3 + d2 + d1);
1938  cells.push_back(start + d3 + d2);
1939  }
1940  }
1941  }
1942 #endif
1943  }
1944 
1945  void
1946  VtuStream::write_cell_single(const unsigned int index,
1947  const unsigned int start,
1948  const unsigned int n_points,
1950  {
1951  (void)index;
1952 
1953  static const std::array<unsigned int, 5> table = {{0, 1, 3, 2, 4}};
1954 
1955 #if !defined(DEAL_II_WITH_ZLIB)
1956  for (unsigned int i = 0; i < n_points; ++i)
1957  stream << '\t'
1958  << start +
1959  (reference_cell == ReferenceCells::Pyramid ? table[i] : i);
1960  stream << '\n';
1961 #else
1962  for (unsigned int i = 0; i < n_points; ++i)
1963  cells.push_back(
1964  start + (reference_cell == ReferenceCells::Pyramid ? table[i] : i));
1965 #endif
1966  }
1967 
1968  template <int dim>
1969  void
1970  VtuStream::write_high_order_cell(const unsigned int,
1971  const unsigned int start,
1972  const std::vector<unsigned> &connectivity)
1973  {
1974 #if !defined(DEAL_II_WITH_ZLIB)
1975  for (const auto &c : connectivity)
1976  stream << '\t' << start + c;
1977  stream << '\n';
1978 #else
1979  for (const auto &c : connectivity)
1980  cells.push_back(start + c);
1981 #endif
1982  }
1983 
1984  void
1985  VtuStream::flush_cells()
1986  {
1987 #ifdef DEAL_II_WITH_ZLIB
1988  // compress the data we have in memory and write them to the stream. then
1989  // release the data
1990  *this << cells << '\n';
1991  cells.clear();
1992 #endif
1993  }
1994 
1995 
1996  template <typename T>
1997  std::ostream &
1998  VtuStream::operator<<(const std::vector<T> &data)
1999  {
2000 #ifdef DEAL_II_WITH_ZLIB
2001  // compress the data we have in memory and write them to the stream. then
2002  // release the data
2003  write_compressed_block(data, flags, stream);
2004 #else
2005  for (unsigned int i = 0; i < data.size(); ++i)
2006  stream << data[i] << ' ';
2007 #endif
2008 
2009  return stream;
2010  }
2011 } // namespace
2012 
2013 
2014 
2015 namespace DataOutBase
2016 {
2017  const unsigned int Deal_II_IntermediateFlags::format_version = 4;
2018 
2019 
2020  template <int dim, int spacedim>
2021  const unsigned int Patch<dim, spacedim>::space_dim;
2022 
2023 
2024  template <int dim, int spacedim>
2025  const unsigned int Patch<dim, spacedim>::no_neighbor;
2026 
2027 
2028  template <int dim, int spacedim>
2030  : patch_index(no_neighbor)
2031  , n_subdivisions(1)
2032  , points_are_available(false)
2034  // all the other data has a constructor of its own, except for the "neighbors"
2035  // field, which we set to invalid values.
2036  {
2037  for (unsigned int i : GeometryInfo<dim>::face_indices())
2038  neighbors[i] = no_neighbor;
2039 
2040  AssertIndexRange(dim, spacedim + 1);
2041  Assert(spacedim <= 3, ExcNotImplemented());
2042  }
2043 
2044 
2045 
2046  template <int dim, int spacedim>
2047  bool
2049  {
2050  if (reference_cell != patch.reference_cell)
2051  return false;
2052 
2053  // TODO: make tolerance relative
2054  const double epsilon = 3e-16;
2055  for (const unsigned int i : GeometryInfo<dim>::vertex_indices())
2056  if (vertices[i].distance(patch.vertices[i]) > epsilon)
2057  return false;
2058 
2059  for (unsigned int i : GeometryInfo<dim>::face_indices())
2060  if (neighbors[i] != patch.neighbors[i])
2061  return false;
2062 
2063  if (patch_index != patch.patch_index)
2064  return false;
2065 
2066  if (n_subdivisions != patch.n_subdivisions)
2067  return false;
2068 
2069  if (points_are_available != patch.points_are_available)
2070  return false;
2071 
2072  if (data.n_rows() != patch.data.n_rows())
2073  return false;
2074 
2075  if (data.n_cols() != patch.data.n_cols())
2076  return false;
2077 
2078  for (unsigned int i = 0; i < data.n_rows(); ++i)
2079  for (unsigned int j = 0; j < data.n_cols(); ++j)
2080  if (data[i][j] != patch.data[i][j])
2081  return false;
2082 
2083  return true;
2084  }
2085 
2086 
2087 
2088  template <int dim, int spacedim>
2089  std::size_t
2091  {
2092  return (sizeof(vertices) / sizeof(vertices[0]) *
2094  sizeof(neighbors) / sizeof(neighbors[0]) *
2097  MemoryConsumption::memory_consumption(n_subdivisions) +
2099  MemoryConsumption::memory_consumption(points_are_available) +
2100  sizeof(reference_cell));
2101  }
2102 
2103 
2104 
2105  template <int dim, int spacedim>
2106  void
2108  {
2109  std::swap(vertices, other_patch.vertices);
2110  std::swap(neighbors, other_patch.neighbors);
2111  std::swap(patch_index, other_patch.patch_index);
2112  std::swap(n_subdivisions, other_patch.n_subdivisions);
2113  data.swap(other_patch.data);
2114  std::swap(points_are_available, other_patch.points_are_available);
2115  std::swap(reference_cell, other_patch.reference_cell);
2116  }
2117 
2118 
2119 
2120  template <int spacedim>
2121  const unsigned int Patch<0, spacedim>::space_dim;
2122 
2123 
2124  template <int spacedim>
2125  const unsigned int Patch<0, spacedim>::no_neighbor;
2126 
2127 
2128  template <int spacedim>
2129  unsigned int Patch<0, spacedim>::neighbors[1] = {
2131 
2132  template <int spacedim>
2133  const unsigned int Patch<0, spacedim>::n_subdivisions = 1;
2134 
2135  template <int spacedim>
2138 
2139  template <int spacedim>
2141  : patch_index(no_neighbor)
2142  , points_are_available(false)
2143  {
2144  Assert(spacedim <= 3, ExcNotImplemented());
2145  }
2146 
2147 
2148 
2149  template <int spacedim>
2150  bool
2152  {
2153  const unsigned int dim = 0;
2154 
2155  // TODO: make tolerance relative
2156  const double epsilon = 3e-16;
2157  for (const unsigned int i : GeometryInfo<dim>::vertex_indices())
2158  if (vertices[i].distance(patch.vertices[i]) > epsilon)
2159  return false;
2160 
2161  if (patch_index != patch.patch_index)
2162  return false;
2163 
2165  return false;
2166 
2167  if (data.n_rows() != patch.data.n_rows())
2168  return false;
2169 
2170  if (data.n_cols() != patch.data.n_cols())
2171  return false;
2172 
2173  for (unsigned int i = 0; i < data.n_rows(); ++i)
2174  for (unsigned int j = 0; j < data.n_cols(); ++j)
2175  if (data[i][j] != patch.data[i][j])
2176  return false;
2177 
2178  return true;
2179  }
2180 
2181 
2182 
2183  template <int spacedim>
2184  std::size_t
2186  {
2187  return (sizeof(vertices) / sizeof(vertices[0]) *
2191  }
2192 
2193 
2194 
2195  template <int spacedim>
2196  void
2198  {
2199  std::swap(vertices, other_patch.vertices);
2200  std::swap(patch_index, other_patch.patch_index);
2201  data.swap(other_patch.data);
2203  }
2204 
2205 
2206 
2207  UcdFlags::UcdFlags(const bool write_preamble)
2208  : write_preamble(write_preamble)
2209  {}
2210 
2211 
2212 
2214  {
2215  space_dimension_labels.emplace_back("x");
2216  space_dimension_labels.emplace_back("y");
2217  space_dimension_labels.emplace_back("z");
2218  }
2219 
2220 
2221 
2222  GnuplotFlags::GnuplotFlags(const std::vector<std::string> &labels)
2223  : space_dimension_labels(labels)
2224  {}
2225 
2226 
2227 
2228  std::size_t
2230  {
2232  }
2233 
2234 
2235 
2236  PovrayFlags::PovrayFlags(const bool smooth,
2237  const bool bicubic_patch,
2238  const bool external_data)
2239  : smooth(smooth)
2240  , bicubic_patch(bicubic_patch)
2241  , external_data(external_data)
2242  {}
2243 
2244 
2245  DataOutFilterFlags::DataOutFilterFlags(const bool filter_duplicate_vertices,
2246  const bool xdmf_hdf5_output)
2247  : filter_duplicate_vertices(filter_duplicate_vertices)
2248  , xdmf_hdf5_output(xdmf_hdf5_output)
2249  {}
2250 
2251 
2252  void
2254  {
2255  prm.declare_entry(
2256  "Filter duplicate vertices",
2257  "false",
2258  Patterns::Bool(),
2259  "Whether to remove duplicate vertex values. deal.II duplicates "
2260  "vertices once for each adjacent cell so that it can output "
2261  "discontinuous quantities for which there may be more than one "
2262  "value for each vertex position. Setting this flag to "
2263  "'true' will merge all of these values by selecting a "
2264  "random one and outputting this as 'the' value for the vertex. "
2265  "As long as the data to be output corresponds to continuous "
2266  "fields, merging vertices has no effect. On the other hand, "
2267  "if the data to be output corresponds to discontinuous fields "
2268  "(either because you are using a discontinuous finite element, "
2269  "or because you are using a DataPostprocessor that yields "
2270  "discontinuous data, or because the data to be output has been "
2271  "produced by entirely different means), then the data in the "
2272  "output file no longer faithfully represents the underlying data "
2273  "because the discontinuous field has been replaced by a "
2274  "continuous one. Note also that the filtering can not occur "
2275  "on processor boundaries. Thus, a filtered discontinuous field "
2276  "looks like a continuous field inside of a subdomain, "
2277  "but like a discontinuous field at the subdomain boundary."
2278  "\n\n"
2279  "In any case, filtering results in drastically smaller output "
2280  "files (smaller by about a factor of 2^dim).");
2281  prm.declare_entry(
2282  "XDMF HDF5 output",
2283  "false",
2284  Patterns::Bool(),
2285  "Whether the data will be used in an XDMF/HDF5 combination.");
2286  }
2287 
2288 
2289 
2290  void
2292  {
2293  filter_duplicate_vertices = prm.get_bool("Filter duplicate vertices");
2294  xdmf_hdf5_output = prm.get_bool("XDMF HDF5 output");
2295  }
2296 
2297 
2298 
2299  DXFlags::DXFlags(const bool write_neighbors,
2300  const bool int_binary,
2301  const bool coordinates_binary,
2302  const bool data_binary)
2303  : write_neighbors(write_neighbors)
2304  , int_binary(int_binary)
2305  , coordinates_binary(coordinates_binary)
2306  , data_binary(data_binary)
2307  , data_double(false)
2308  {}
2309 
2310 
2311  void
2313  {
2314  prm.declare_entry("Write neighbors",
2315  "true",
2316  Patterns::Bool(),
2317  "A boolean field indicating whether neighborship "
2318  "information between cells is to be written to the "
2319  "OpenDX output file");
2320  prm.declare_entry("Integer format",
2321  "ascii",
2322  Patterns::Selection("ascii|32|64"),
2323  "Output format of integer numbers, which is "
2324  "either a text representation (ascii) or binary integer "
2325  "values of 32 or 64 bits length");
2326  prm.declare_entry("Coordinates format",
2327  "ascii",
2328  Patterns::Selection("ascii|32|64"),
2329  "Output format of vertex coordinates, which is "
2330  "either a text representation (ascii) or binary "
2331  "floating point values of 32 or 64 bits length");
2332  prm.declare_entry("Data format",
2333  "ascii",
2334  Patterns::Selection("ascii|32|64"),
2335  "Output format of data values, which is "
2336  "either a text representation (ascii) or binary "
2337  "floating point values of 32 or 64 bits length");
2338  }
2339 
2340 
2341 
2342  void
2344  {
2345  write_neighbors = prm.get_bool("Write neighbors");
2346  // TODO:[GK] Read the new parameters
2347  }
2348 
2349 
2350 
2351  void
2353  {
2354  prm.declare_entry("Write preamble",
2355  "true",
2356  Patterns::Bool(),
2357  "A flag indicating whether a comment should be "
2358  "written to the beginning of the output file "
2359  "indicating date and time of creation as well "
2360  "as the creating program");
2361  }
2362 
2363 
2364 
2365  void
2367  {
2368  write_preamble = prm.get_bool("Write preamble");
2369  }
2370 
2371 
2372 
2373  SvgFlags::SvgFlags(const unsigned int height_vector,
2374  const int azimuth_angle,
2375  const int polar_angle,
2376  const unsigned int line_thickness,
2377  const bool margin,
2378  const bool draw_colorbar)
2379  : height(4000)
2380  , width(0)
2381  , height_vector(height_vector)
2382  , azimuth_angle(azimuth_angle)
2383  , polar_angle(polar_angle)
2384  , line_thickness(line_thickness)
2385  , margin(margin)
2386  , draw_colorbar(draw_colorbar)
2387  {}
2388 
2389 
2390 
2391  void
2393  {
2394  prm.declare_entry("Use smooth triangles",
2395  "false",
2396  Patterns::Bool(),
2397  "A flag indicating whether POVRAY should use smoothed "
2398  "triangles instead of the usual ones");
2399  prm.declare_entry("Use bicubic patches",
2400  "false",
2401  Patterns::Bool(),
2402  "Whether POVRAY should use bicubic patches");
2403  prm.declare_entry("Include external file",
2404  "true",
2405  Patterns::Bool(),
2406  "Whether camera and lighting information should "
2407  "be put into an external file \"data.inc\" or into "
2408  "the POVRAY input file");
2409  }
2410 
2411 
2412 
2413  void
2415  {
2416  smooth = prm.get_bool("Use smooth triangles");
2417  bicubic_patch = prm.get_bool("Use bicubic patches");
2418  external_data = prm.get_bool("Include external file");
2419  }
2420 
2421 
2422 
2423  EpsFlags::EpsFlags(const unsigned int height_vector,
2424  const unsigned int color_vector,
2425  const SizeType size_type,
2426  const unsigned int size,
2427  const double line_width,
2428  const double azimut_angle,
2429  const double turn_angle,
2430  const double z_scaling,
2431  const bool draw_mesh,
2432  const bool draw_cells,
2433  const bool shade_cells,
2434  const ColorFunction color_function)
2435  : height_vector(height_vector)
2436  , color_vector(color_vector)
2437  , size_type(size_type)
2438  , size(size)
2439  , line_width(line_width)
2440  , azimut_angle(azimut_angle)
2441  , turn_angle(turn_angle)
2442  , z_scaling(z_scaling)
2443  , draw_mesh(draw_mesh)
2444  , draw_cells(draw_cells)
2445  , shade_cells(shade_cells)
2446  , color_function(color_function)
2447  {}
2448 
2449 
2450 
2453  const double xmin,
2454  const double xmax)
2455  {
2456  RgbValues rgb_values = {0, 0, 0};
2457 
2458  // A difficult color scale:
2459  // xmin = black (1)
2460  // 3/4*xmin+1/4*xmax = blue (2)
2461  // 1/2*xmin+1/2*xmax = green (3)
2462  // 1/4*xmin+3/4*xmax = red (4)
2463  // xmax = white (5)
2464  // Makes the following color functions:
2465  //
2466  // red green blue
2467  // __
2468  // / /\ / /\ /
2469  // ____/ __/ \/ / \__/
2470 
2471  // { 0 (1) - (3)
2472  // r = { ( 4*x-2*xmin+2*xmax)/(xmax-xmin) (3) - (4)
2473  // { 1 (4) - (5)
2474  //
2475  // { 0 (1) - (2)
2476  // g = { ( 4*x-3*xmin- xmax)/(xmax-xmin) (2) - (3)
2477  // { (-4*x+ xmin+3*xmax)/(xmax-xmin) (3) - (4)
2478  // { ( 4*x- xmin-3*xmax)/(xmax-xmin) (4) - (5)
2479  //
2480  // { ( 4*x-4*xmin )/(xmax-xmin) (1) - (2)
2481  // b = { (-4*x+2*xmin+2*xmax)/(xmax-xmin) (2) - (3)
2482  // { 0 (3) - (4)
2483  // { ( 4*x- xmin-3*xmax)/(xmax-xmin) (4) - (5)
2484 
2485  double sum = xmax + xmin;
2486  double sum13 = xmin + 3 * xmax;
2487  double sum22 = 2 * xmin + 2 * xmax;
2488  double sum31 = 3 * xmin + xmax;
2489  double dif = xmax - xmin;
2490  double rezdif = 1.0 / dif;
2491 
2492  int where;
2493 
2494  if (x < (sum31) / 4)
2495  where = 0;
2496  else if (x < (sum22) / 4)
2497  where = 1;
2498  else if (x < (sum13) / 4)
2499  where = 2;
2500  else
2501  where = 3;
2502 
2503  if (dif != 0)
2504  {
2505  switch (where)
2506  {
2507  case 0:
2508  rgb_values.red = 0;
2509  rgb_values.green = 0;
2510  rgb_values.blue = (x - xmin) * 4. * rezdif;
2511  break;
2512  case 1:
2513  rgb_values.red = 0;
2514  rgb_values.green = (4 * x - 3 * xmin - xmax) * rezdif;
2515  rgb_values.blue = (sum22 - 4. * x) * rezdif;
2516  break;
2517  case 2:
2518  rgb_values.red = (4 * x - 2 * sum) * rezdif;
2519  rgb_values.green = (xmin + 3 * xmax - 4 * x) * rezdif;
2520  rgb_values.blue = 0;
2521  break;
2522  case 3:
2523  rgb_values.red = 1;
2524  rgb_values.green = (4 * x - xmin - 3 * xmax) * rezdif;
2525  rgb_values.blue = (4. * x - sum13) * rezdif;
2526  break;
2527  default:
2528  break;
2529  }
2530  }
2531  else // White
2532  rgb_values.red = rgb_values.green = rgb_values.blue = 1;
2533 
2534  return rgb_values;
2535  }
2536 
2537 
2538 
2541  const double xmin,
2542  const double xmax)
2543  {
2544  EpsFlags::RgbValues rgb_values;
2545  rgb_values.red = rgb_values.blue = rgb_values.green =
2546  (x - xmin) / (xmax - xmin);
2547  return rgb_values;
2548  }
2549 
2550 
2551 
2554  const double xmin,
2555  const double xmax)
2556  {
2557  EpsFlags::RgbValues rgb_values;
2558  rgb_values.red = rgb_values.blue = rgb_values.green =
2559  1 - (x - xmin) / (xmax - xmin);
2560  return rgb_values;
2561  }
2562 
2563 
2564 
2565  void
2567  {
2568  prm.declare_entry("Index of vector for height",
2569  "0",
2571  "Number of the input vector that is to be used to "
2572  "generate height information");
2573  prm.declare_entry("Index of vector for color",
2574  "0",
2576  "Number of the input vector that is to be used to "
2577  "generate color information");
2578  prm.declare_entry("Scale to width or height",
2579  "width",
2580  Patterns::Selection("width|height"),
2581  "Whether width or height should be scaled to match "
2582  "the given size");
2583  prm.declare_entry("Size (width or height) in eps units",
2584  "300",
2586  "The size (width or height) to which the eps output "
2587  "file is to be scaled");
2588  prm.declare_entry("Line widths in eps units",
2589  "0.5",
2590  Patterns::Double(),
2591  "The width in which the postscript renderer is to "
2592  "plot lines");
2593  prm.declare_entry("Azimut angle",
2594  "60",
2595  Patterns::Double(0, 180),
2596  "Angle of the viewing position against the vertical "
2597  "axis");
2598  prm.declare_entry("Turn angle",
2599  "30",
2600  Patterns::Double(0, 360),
2601  "Angle of the viewing direction against the y-axis");
2602  prm.declare_entry("Scaling for z-axis",
2603  "1",
2604  Patterns::Double(),
2605  "Scaling for the z-direction relative to the scaling "
2606  "used in x- and y-directions");
2607  prm.declare_entry("Draw mesh lines",
2608  "true",
2609  Patterns::Bool(),
2610  "Whether the mesh lines, or only the surface should be "
2611  "drawn");
2612  prm.declare_entry("Fill interior of cells",
2613  "true",
2614  Patterns::Bool(),
2615  "Whether only the mesh lines, or also the interior of "
2616  "cells should be plotted. If this flag is false, then "
2617  "one can see through the mesh");
2618  prm.declare_entry("Color shading of interior of cells",
2619  "true",
2620  Patterns::Bool(),
2621  "Whether the interior of cells shall be shaded");
2622  prm.declare_entry("Color function",
2623  "default",
2625  "default|grey scale|reverse grey scale"),
2626  "Name of a color function used to colorize mesh lines "
2627  "and/or cell interiors");
2628  }
2629 
2630 
2631 
2632  void
2634  {
2635  height_vector = prm.get_integer("Index of vector for height");
2636  color_vector = prm.get_integer("Index of vector for color");
2637  if (prm.get("Scale to width or height") == "width")
2638  size_type = width;
2639  else
2640  size_type = height;
2641  size = prm.get_integer("Size (width or height) in eps units");
2642  line_width = prm.get_double("Line widths in eps units");
2643  azimut_angle = prm.get_double("Azimut angle");
2644  turn_angle = prm.get_double("Turn angle");
2645  z_scaling = prm.get_double("Scaling for z-axis");
2646  draw_mesh = prm.get_bool("Draw mesh lines");
2647  draw_cells = prm.get_bool("Fill interior of cells");
2648  shade_cells = prm.get_bool("Color shading of interior of cells");
2649  if (prm.get("Color function") == "default")
2651  else if (prm.get("Color function") == "grey scale")
2653  else if (prm.get("Color function") == "reverse grey scale")
2655  else
2656  // we shouldn't get here, since the parameter object should already have
2657  // checked that the given value is valid
2658  Assert(false, ExcInternalError());
2659  }
2661 
2662 
2663  TecplotFlags::TecplotFlags(const char *zone_name, const double solution_time)
2664  : zone_name(zone_name)
2665  , solution_time(solution_time)
2666  {}
2667 
2668 
2669 
2670  std::size_t
2672  {
2673  return sizeof(*this) + MemoryConsumption::memory_consumption(zone_name);
2674  }
2675 
2676 
2677 
2678  VtkFlags::VtkFlags(const double time,
2679  const unsigned int cycle,
2680  const bool print_date_and_time,
2681  const VtkFlags::ZlibCompressionLevel compression_level,
2682  const bool write_higher_order_cells,
2683  const std::map<std::string, std::string> &physical_units)
2684  : time(time)
2685  , cycle(cycle)
2686  , print_date_and_time(print_date_and_time)
2687  , compression_level(compression_level)
2688  , write_higher_order_cells(write_higher_order_cells)
2689  , physical_units(physical_units)
2690  {}
2692 
2693 
2694  OutputFormat
2695  parse_output_format(const std::string &format_name)
2696  {
2697  if (format_name == "none")
2698  return none;
2699 
2700  if (format_name == "dx")
2701  return dx;
2703  if (format_name == "ucd")
2704  return ucd;
2705 
2706  if (format_name == "gnuplot")
2707  return gnuplot;
2708 
2709  if (format_name == "povray")
2710  return povray;
2711 
2712  if (format_name == "eps")
2713  return eps;
2714 
2715  if (format_name == "gmv")
2716  return gmv;
2717 
2718  if (format_name == "tecplot")
2719  return tecplot;
2720 
2721  if (format_name == "tecplot_binary")
2722  return tecplot_binary;
2723 
2724  if (format_name == "vtk")
2725  return vtk;
2726 
2727  if (format_name == "vtu")
2728  return vtu;
2729 
2730  if (format_name == "deal.II intermediate")
2731  return deal_II_intermediate;
2732 
2733  if (format_name == "hdf5")
2734  return hdf5;
2735 
2736  AssertThrow(false,
2737  ExcMessage("The given file format name is not recognized: <" +
2738  format_name + ">"));
2740  // return something invalid
2741  return OutputFormat(-1);
2742  }
2743 
2744 
2745 
2746  std::string
2748  {
2749  return "none|dx|ucd|gnuplot|povray|eps|gmv|tecplot|tecplot_binary|vtk|vtu|hdf5|svg|deal.II intermediate";
2750  }
2751 
2752 
2753 
2754  std::string
2755  default_suffix(const OutputFormat output_format)
2756  {
2757  switch (output_format)
2758  {
2759  case none:
2760  return "";
2761  case dx:
2762  return ".dx";
2763  case ucd:
2764  return ".inp";
2765  case gnuplot:
2766  return ".gnuplot";
2767  case povray:
2768  return ".pov";
2769  case eps:
2770  return ".eps";
2771  case gmv:
2772  return ".gmv";
2773  case tecplot:
2774  return ".dat";
2775  case tecplot_binary:
2776  return ".plt";
2777  case vtk:
2778  return ".vtk";
2779  case vtu:
2780  return ".vtu";
2781  case deal_II_intermediate:
2782  return ".d2";
2783  case hdf5:
2784  return ".h5";
2785  case svg:
2786  return ".svg";
2787  default:
2788  Assert(false, ExcNotImplemented());
2789  return "";
2790  }
2791  }
2792 
2793 
2794  //----------------------------------------------------------------------//
2795 
2796  template <int dim, int spacedim, typename StreamType>
2797  void
2798  write_nodes(const std::vector<Patch<dim, spacedim>> &patches, StreamType &out)
2799  {
2800  Assert(dim <= 3, ExcNotImplemented());
2801  unsigned int count = 0;
2802 
2803  static const std::array<unsigned int, 5> table = {{0, 1, 3, 2, 4}};
2804 
2805  for (const auto &patch : patches)
2806  {
2807  // special treatment of non-hypercube cells
2808  if (patch.reference_cell != ReferenceCells::get_hypercube<dim>())
2809  {
2810  for (unsigned int point_no = 0; point_no < patch.data.n_cols();
2811  ++point_no)
2812  out.write_point(count++,
2813  get_node_location(patch,
2814  (patch.reference_cell ==
2816  table[point_no] :
2817  point_no)));
2818  }
2819  else
2820  {
2821  const unsigned int n_subdivisions = patch.n_subdivisions;
2822  const unsigned int n = n_subdivisions + 1;
2823 
2824  switch (dim)
2825  {
2826  case 0:
2827  out.write_point(count++,
2828  get_equispaced_location(patch,
2829  {},
2830  n_subdivisions));
2831  break;
2832  case 1:
2833  for (unsigned int i1 = 0; i1 < n; ++i1)
2834  out.write_point(count++,
2835  get_equispaced_location(patch,
2836  {i1},
2837  n_subdivisions));
2838  break;
2839  case 2:
2840  for (unsigned int i2 = 0; i2 < n; ++i2)
2841  for (unsigned int i1 = 0; i1 < n; ++i1)
2842  out.write_point(count++,
2843  get_equispaced_location(patch,
2844  {i1, i2},
2845  n_subdivisions));
2846  break;
2847  case 3:
2848  for (unsigned int i3 = 0; i3 < n; ++i3)
2849  for (unsigned int i2 = 0; i2 < n; ++i2)
2850  for (unsigned int i1 = 0; i1 < n; ++i1)
2851  out.write_point(count++,
2852  get_equispaced_location(
2853  patch, {i1, i2, i3}, n_subdivisions));
2854  break;
2855 
2856  default:
2857  Assert(false, ExcInternalError());
2858  }
2859  }
2860  }
2861  out.flush_points();
2862  }
2863 
2864  template <int dim, int spacedim, typename StreamType>
2865  void
2866  write_cells(const std::vector<Patch<dim, spacedim>> &patches, StreamType &out)
2867  {
2868  Assert(dim <= 3, ExcNotImplemented());
2869  unsigned int count = 0;
2870  unsigned int first_vertex_of_patch = 0;
2871  for (const auto &patch : patches)
2872  {
2873  // special treatment of simplices since they are not subdivided
2874  if (patch.reference_cell != ReferenceCells::get_hypercube<dim>())
2875  {
2876  out.write_cell_single(count++,
2877  first_vertex_of_patch,
2878  patch.data.n_cols(),
2879  patch.reference_cell);
2880  first_vertex_of_patch += patch.data.n_cols();
2881  }
2882  else
2883  {
2884  const unsigned int n_subdivisions = patch.n_subdivisions;
2885  const unsigned int n = n_subdivisions + 1;
2886  // Length of loops in all dimensions
2887  const unsigned int n1 = (dim > 0) ? n_subdivisions : 1;
2888  const unsigned int n2 = (dim > 1) ? n_subdivisions : 1;
2889  const unsigned int n3 = (dim > 2) ? n_subdivisions : 1;
2890  // Offsets of outer loops
2891  const unsigned int d1 = 1;
2892  const unsigned int d2 = n;
2893  const unsigned int d3 = n * n;
2894  for (unsigned int i3 = 0; i3 < n3; ++i3)
2895  for (unsigned int i2 = 0; i2 < n2; ++i2)
2896  for (unsigned int i1 = 0; i1 < n1; ++i1)
2897  {
2898  const unsigned int offset =
2899  first_vertex_of_patch + i3 * d3 + i2 * d2 + i1 * d1;
2900  // First write line in x direction
2901  out.template write_cell<dim>(count++, offset, d1, d2, d3);
2902  }
2903  // finally update the number of the first vertex of this patch
2904  first_vertex_of_patch +=
2905  Utilities::fixed_power<dim>(n_subdivisions + 1);
2906  }
2907  }
2909  out.flush_cells();
2910  }
2911 
2912  template <int dim, int spacedim, typename StreamType>
2913  void
2914  write_high_order_cells(const std::vector<Patch<dim, spacedim>> &patches,
2915  StreamType & out)
2916  {
2917  Assert(dim <= 3 && dim > 1, ExcNotImplemented());
2918  unsigned int first_vertex_of_patch = 0;
2919  unsigned int count = 0;
2920  // Array to hold all the node numbers of a cell
2921  std::vector<unsigned> connectivity;
2922  // Array to hold cell order in each dimension
2923  std::array<unsigned, dim> cell_order;
2924 
2925  for (const auto &patch : patches)
2926  {
2927  if (patch.reference_cell != ReferenceCells::get_hypercube<dim>())
2928  {
2929  connectivity.resize(patch.data.n_cols());
2930 
2931  for (unsigned int i = 0; i < patch.data.n_cols(); ++i)
2932  connectivity[i] = i;
2934  out.template write_high_order_cell<dim>(count++,
2935  first_vertex_of_patch,
2936  connectivity);
2937 
2938  first_vertex_of_patch += patch.data.n_cols();
2939  }
2940  else
2941  {
2942  const unsigned int n_subdivisions = patch.n_subdivisions;
2943  const unsigned int n = n_subdivisions + 1;
2944 
2945  cell_order.fill(n_subdivisions);
2946  connectivity.resize(Utilities::fixed_power<dim>(n));
2947 
2948  // Length of loops in all dimensons
2949  const unsigned int n1 = (dim > 0) ? n_subdivisions : 0;
2950  const unsigned int n2 = (dim > 1) ? n_subdivisions : 0;
2951  const unsigned int n3 = (dim > 2) ? n_subdivisions : 0;
2952  // Offsets of outer loops
2953  const unsigned int d1 = 1;
2954  const unsigned int d2 = n;
2955  const unsigned int d3 = n * n;
2956  for (unsigned int i3 = 0; i3 <= n3; ++i3)
2957  for (unsigned int i2 = 0; i2 <= n2; ++i2)
2958  for (unsigned int i1 = 0; i1 <= n1; ++i1)
2959  {
2960  const unsigned int local_index =
2961  i3 * d3 + i2 * d2 + i1 * d1;
2962  const unsigned int connectivity_index =
2963  vtk_point_index_from_ijk(i1, i2, i3, cell_order);
2964  connectivity[connectivity_index] = local_index;
2965  }
2966 
2967  out.template write_high_order_cell<dim>(count++,
2968  first_vertex_of_patch,
2969  connectivity);
2970 
2971  // finally update the number of the first vertex of this patch
2972  first_vertex_of_patch += Utilities::fixed_power<dim>(n);
2973  }
2974  }
2975 
2976  out.flush_cells();
2977  }
2978 
2979 
2980  template <int dim, int spacedim, class StreamType>
2981  void
2982  write_data(const std::vector<Patch<dim, spacedim>> &patches,
2983  unsigned int n_data_sets,
2984  const bool double_precision,
2985  StreamType & out)
2986  {
2987  Assert(dim <= 3, ExcNotImplemented());
2988  unsigned int count = 0;
2989 
2990  for (const auto &patch : patches)
2991  {
2992  const unsigned int n_subdivisions = patch.n_subdivisions;
2993  const unsigned int n = n_subdivisions + 1;
2994  // Length of loops in all dimensions
2995  Assert((patch.data.n_rows() == n_data_sets &&
2996  !patch.points_are_available) ||
2997  (patch.data.n_rows() == n_data_sets + spacedim &&
3000  (n_data_sets + spacedim) :
3001  n_data_sets,
3002  patch.data.n_rows()));
3003  Assert(patch.data.n_cols() == Utilities::fixed_power<dim>(n),
3004  ExcInvalidDatasetSize(patch.data.n_cols(), n));
3005 
3006  std::vector<float> floats(n_data_sets);
3007  std::vector<double> doubles(n_data_sets);
3008 
3009  // Data is already in lexicographic ordering
3010  for (unsigned int i = 0; i < Utilities::fixed_power<dim>(n);
3011  ++i, ++count)
3012  if (double_precision)
3013  {
3014  for (unsigned int data_set = 0; data_set < n_data_sets;
3015  ++data_set)
3016  doubles[data_set] = patch.data(data_set, i);
3017  out.write_dataset(count, doubles);
3018  }
3019  else
3020  {
3021  for (unsigned int data_set = 0; data_set < n_data_sets;
3022  ++data_set)
3023  floats[data_set] = patch.data(data_set, i);
3024  out.write_dataset(count, floats);
3025  }
3026  }
3027  }
3028 
3029 
3030 
3031  namespace
3032  {
3042  svg_project_point(Point<3> point,
3043  Point<3> camera_position,
3044  Point<3> camera_direction,
3045  Point<3> camera_horizontal,
3046  float camera_focus)
3047  {
3048  Point<3> camera_vertical;
3049  camera_vertical[0] = camera_horizontal[1] * camera_direction[2] -
3050  camera_horizontal[2] * camera_direction[1];
3051  camera_vertical[1] = camera_horizontal[2] * camera_direction[0] -
3052  camera_horizontal[0] * camera_direction[2];
3053  camera_vertical[2] = camera_horizontal[0] * camera_direction[1] -
3054  camera_horizontal[1] * camera_direction[0];
3055 
3056  float phi;
3057  phi = camera_focus;
3058  phi /= (point[0] - camera_position[0]) * camera_direction[0] +
3059  (point[1] - camera_position[1]) * camera_direction[1] +
3060  (point[2] - camera_position[2]) * camera_direction[2];
3061 
3062  Point<3> projection;
3063  projection[0] =
3064  camera_position[0] + phi * (point[0] - camera_position[0]);
3065  projection[1] =
3066  camera_position[1] + phi * (point[1] - camera_position[1]);
3067  projection[2] =
3068  camera_position[2] + phi * (point[2] - camera_position[2]);
3069 
3070  Point<2> projection_decomposition;
3071  projection_decomposition[0] = (projection[0] - camera_position[0] -
3072  camera_focus * camera_direction[0]) *
3073  camera_horizontal[0];
3074  projection_decomposition[0] += (projection[1] - camera_position[1] -
3075  camera_focus * camera_direction[1]) *
3076  camera_horizontal[1];
3077  projection_decomposition[0] += (projection[2] - camera_position[2] -
3078  camera_focus * camera_direction[2]) *
3079  camera_horizontal[2];
3080 
3081  projection_decomposition[1] = (projection[0] - camera_position[0] -
3082  camera_focus * camera_direction[0]) *
3083  camera_vertical[0];
3084  projection_decomposition[1] += (projection[1] - camera_position[1] -
3085  camera_focus * camera_direction[1]) *
3086  camera_vertical[1];
3087  projection_decomposition[1] += (projection[2] - camera_position[2] -
3088  camera_focus * camera_direction[2]) *
3089  camera_vertical[2];
3090 
3091  return projection_decomposition;
3092  }
3093 
3094 
3099  Point<6>
3100  svg_get_gradient_parameters(Point<3> points[])
3101  {
3102  Point<3> v_min, v_max, v_inter;
3103 
3104  // Use the Bubblesort algorithm to sort the points with respect to the
3105  // third coordinate
3106  for (int i = 0; i < 2; ++i)
3107  {
3108  for (int j = 0; j < 2 - i; ++j)
3109  {
3110  if (points[j][2] > points[j + 1][2])
3111  {
3112  Point<3> temp = points[j];
3113  points[j] = points[j + 1];
3114  points[j + 1] = temp;
3115  }
3116  }
3117  }
3118 
3119  // save the related three-dimensional vectors v_min, v_inter, and v_max
3120  v_min = points[0];
3121  v_inter = points[1];
3122  v_max = points[2];
3123 
3124  Point<2> A[2];
3125  Point<2> b, gradient;
3126 
3127  // determine the plane offset c
3128  A[0][0] = v_max[0] - v_min[0];
3129  A[0][1] = v_inter[0] - v_min[0];
3130  A[1][0] = v_max[1] - v_min[1];
3131  A[1][1] = v_inter[1] - v_min[1];
3132 
3133  b[0] = -v_min[0];
3134  b[1] = -v_min[1];
3135 
3136  double x, sum;
3137  bool col_change = false;
3138 
3139  if (A[0][0] == 0)
3140  {
3141  col_change = true;
3142 
3143  A[0][0] = A[0][1];
3144  A[0][1] = 0;
3145 
3146  double temp = A[1][0];
3147  A[1][0] = A[1][1];
3148  A[1][1] = temp;
3149  }
3150 
3151  for (unsigned int k = 0; k < 1; ++k)
3152  {
3153  for (unsigned int i = k + 1; i < 2; ++i)
3154  {
3155  x = A[i][k] / A[k][k];
3156 
3157  for (unsigned int j = k + 1; j < 2; ++j)
3158  A[i][j] = A[i][j] - A[k][j] * x;
3159 
3160  b[i] = b[i] - b[k] * x;
3161  }
3162  }
3163 
3164  b[1] = b[1] / A[1][1];
3165 
3166  for (int i = 0; i >= 0; i--)
3167  {
3168  sum = b[i];
3169 
3170  for (unsigned int j = i + 1; j < 2; ++j)
3171  sum = sum - A[i][j] * b[j];
3172 
3173  b[i] = sum / A[i][i];
3174  }
3175 
3176  if (col_change)
3177  {
3178  double temp = b[0];
3179  b[0] = b[1];
3180  b[1] = temp;
3181  }
3182 
3183  double c = b[0] * (v_max[2] - v_min[2]) + b[1] * (v_inter[2] - v_min[2]) +
3184  v_min[2];
3185 
3186  // Determine the first entry of the gradient (phi, cf. documentation)
3187  A[0][0] = v_max[0] - v_min[0];
3188  A[0][1] = v_inter[0] - v_min[0];
3189  A[1][0] = v_max[1] - v_min[1];
3190  A[1][1] = v_inter[1] - v_min[1];
3191 
3192  b[0] = 1.0 - v_min[0];
3193  b[1] = -v_min[1];
3194 
3195  col_change = false;
3196 
3197  if (A[0][0] == 0)
3198  {
3199  col_change = true;
3200 
3201  A[0][0] = A[0][1];
3202  A[0][1] = 0;
3203 
3204  double temp = A[1][0];
3205  A[1][0] = A[1][1];
3206  A[1][1] = temp;
3207  }
3208 
3209  for (unsigned int k = 0; k < 1; ++k)
3210  {
3211  for (unsigned int i = k + 1; i < 2; ++i)
3212  {
3213  x = A[i][k] / A[k][k];
3214 
3215  for (unsigned int j = k + 1; j < 2; ++j)
3216  A[i][j] = A[i][j] - A[k][j] * x;
3217 
3218  b[i] = b[i] - b[k] * x;
3219  }
3220  }
3221 
3222  b[1] = b[1] / A[1][1];
3223 
3224  for (int i = 0; i >= 0; i--)
3225  {
3226  sum = b[i];
3227 
3228  for (unsigned int j = i + 1; j < 2; ++j)
3229  sum = sum - A[i][j] * b[j];
3230 
3231  b[i] = sum / A[i][i];
3232  }
3233 
3234  if (col_change)
3235  {
3236  double temp = b[0];
3237  b[0] = b[1];
3238  b[1] = temp;
3239  }
3240 
3241  gradient[0] = b[0] * (v_max[2] - v_min[2]) +
3242  b[1] * (v_inter[2] - v_min[2]) - c + v_min[2];
3243 
3244  // determine the second entry of the gradient
3245  A[0][0] = v_max[0] - v_min[0];
3246  A[0][1] = v_inter[0] - v_min[0];
3247  A[1][0] = v_max[1] - v_min[1];
3248  A[1][1] = v_inter[1] - v_min[1];
3249 
3250  b[0] = -v_min[0];
3251  b[1] = 1.0 - v_min[1];
3252 
3253  col_change = false;
3254 
3255  if (A[0][0] == 0)
3256  {
3257  col_change = true;
3258 
3259  A[0][0] = A[0][1];
3260  A[0][1] = 0;
3261 
3262  double temp = A[1][0];
3263  A[1][0] = A[1][1];
3264  A[1][1] = temp;
3265  }
3266 
3267  for (unsigned int k = 0; k < 1; ++k)
3268  {
3269  for (unsigned int i = k + 1; i < 2; ++i)
3270  {
3271  x = A[i][k] / A[k][k];
3272 
3273  for (unsigned int j = k + 1; j < 2; ++j)
3274  A[i][j] = A[i][j] - A[k][j] * x;
3275 
3276  b[i] = b[i] - b[k] * x;
3277  }
3278  }
3279 
3280  b[1] = b[1] / A[1][1];
3281 
3282  for (int i = 0; i >= 0; i--)
3283  {
3284  sum = b[i];
3285 
3286  for (unsigned int j = i + 1; j < 2; ++j)
3287  sum = sum - A[i][j] * b[j];
3288 
3289  b[i] = sum / A[i][i];
3290  }
3291 
3292  if (col_change)
3293  {
3294  double temp = b[0];
3295  b[0] = b[1];
3296  b[1] = temp;
3297  }
3298 
3299  gradient[1] = b[0] * (v_max[2] - v_min[2]) +
3300  b[1] * (v_inter[2] - v_min[2]) - c + v_min[2];
3301 
3302  // normalize the gradient
3303  gradient /= gradient.norm();
3304 
3305  const double lambda = -gradient[0] * (v_min[0] - v_max[0]) -
3306  gradient[1] * (v_min[1] - v_max[1]);
3307 
3308  Point<6> gradient_parameters;
3309 
3310  gradient_parameters[0] = v_min[0];
3311  gradient_parameters[1] = v_min[1];
3312 
3313  gradient_parameters[2] = v_min[0] + lambda * gradient[0];
3314  gradient_parameters[3] = v_min[1] + lambda * gradient[1];
3315 
3316  gradient_parameters[4] = v_min[2];
3317  gradient_parameters[5] = v_max[2];
3318 
3319  return gradient_parameters;
3320  }
3321  } // namespace
3322 
3323 
3324 
3325  template <int dim, int spacedim>
3326  void
3328  const std::vector<Patch<dim, spacedim>> &patches,
3329  const std::vector<std::string> & data_names,
3330  const std::vector<
3331  std::tuple<unsigned int,
3332  unsigned int,
3333  std::string,
3335  const UcdFlags &flags,
3336  std::ostream & out)
3337  {
3338  // Note that while in theory dim==0 should be implemented, this is not
3339  // tested, therefore currently not allowed.
3340  AssertThrow(dim > 0, ExcNotImplemented());
3341 
3342  AssertThrow(out.fail() == false, ExcIO());
3343 
3344 #ifndef DEAL_II_WITH_MPI
3345  // verify that there are indeed patches to be written out. most of the
3346  // times, people just forget to call build_patches when there are no
3347  // patches, so a warning is in order. that said, the assertion is disabled
3348  // if we support MPI since then it can happen that on the coarsest mesh, a
3349  // processor simply has no cells it actually owns, and in that case it is
3350  // legit if there are no patches
3351  Assert(patches.size() > 0, ExcNoPatches());
3352 #else
3353  if (patches.size() == 0)
3354  return;
3355 #endif
3356 
3357  const unsigned int n_data_sets = data_names.size();
3358 
3359  UcdStream ucd_out(out, flags);
3360 
3361  // first count the number of cells and cells for later use
3362  unsigned int n_nodes;
3363  unsigned int n_cells;
3364  compute_sizes<dim, spacedim>(patches, n_nodes, n_cells);
3365  //---------------------
3366  // preamble
3367  if (flags.write_preamble)
3368  {
3369  out
3370  << "# This file was generated by the deal.II library." << '\n'
3371  << "# Date = " << Utilities::System::get_date() << '\n'
3372  << "# Time = " << Utilities::System::get_time() << '\n'
3373  << "#" << '\n'
3374  << "# For a description of the UCD format see the AVS Developer's guide."
3375  << '\n'
3376  << "#" << '\n';
3377  }
3378 
3379  // start with ucd data
3380  out << n_nodes << ' ' << n_cells << ' ' << n_data_sets << ' ' << 0
3381  << ' ' // no cell data at present
3382  << 0 // no model data
3383  << '\n';
3384 
3385  write_nodes(patches, ucd_out);
3386  out << '\n';
3387 
3388  write_cells(patches, ucd_out);
3389  out << '\n';
3390 
3391  //---------------------------
3392  // now write data
3393  if (n_data_sets != 0)
3394  {
3395  out << n_data_sets << " "; // number of vectors
3396  for (unsigned int i = 0; i < n_data_sets; ++i)
3397  out << 1 << ' '; // number of components;
3398  // only 1 supported presently
3399  out << '\n';
3400 
3401  for (unsigned int data_set = 0; data_set < n_data_sets; ++data_set)
3402  out << data_names[data_set]
3403  << ",dimensionless" // no units supported at present
3404  << '\n';
3405 
3406  write_data(patches, n_data_sets, true, ucd_out);
3407  }
3408  // make sure everything now gets to disk
3409  out.flush();
3410 
3411  // assert the stream is still ok
3412  AssertThrow(out.fail() == false, ExcIO());
3413  }
3414 
3415 
3416  template <int dim, int spacedim>
3417  void
3419  const std::vector<Patch<dim, spacedim>> &patches,
3420  const std::vector<std::string> & data_names,
3421  const std::vector<
3422  std::tuple<unsigned int,
3423  unsigned int,
3424  std::string,
3426  const DXFlags &flags,
3427  std::ostream & out)
3428  {
3429  // Point output is currently not implemented.
3430  AssertThrow(dim > 0, ExcNotImplemented());
3431 
3432  AssertThrow(out.fail() == false, ExcIO());
3433 
3434 #ifndef DEAL_II_WITH_MPI
3435  // verify that there are indeed patches to be written out. most of the
3436  // times, people just forget to call build_patches when there are no
3437  // patches, so a warning is in order. that said, the assertion is disabled
3438  // if we support MPI since then it can happen that on the coarsest mesh, a
3439  // processor simply has no cells it actually owns, and in that case it is
3440  // legit if there are no patches
3441  Assert(patches.size() > 0, ExcNoPatches());
3442 #else
3443  if (patches.size() == 0)
3444  return;
3445 #endif
3446  // Stream with special features for dx output
3447  DXStream dx_out(out, flags);
3448 
3449  // Variable counting the offset of binary data.
3450  unsigned int offset = 0;
3451 
3452  const unsigned int n_data_sets = data_names.size();
3453 
3454  // first count the number of cells and cells for later use
3455  unsigned int n_nodes;
3456  unsigned int n_cells;
3457  compute_sizes<dim, spacedim>(patches, n_nodes, n_cells);
3458  // start with vertices order is lexicographical, x varying fastest
3459  out << "object \"vertices\" class array type float rank 1 shape "
3460  << spacedim << " items " << n_nodes;
3461 
3462  if (flags.coordinates_binary)
3463  {
3464  out << " lsb ieee data 0" << '\n';
3465  offset += n_nodes * spacedim * sizeof(float);
3466  }
3467  else
3468  {
3469  out << " data follows" << '\n';
3470  write_nodes(patches, dx_out);
3471  }
3472 
3473  //-----------------------------
3474  // first write the coordinates of all vertices
3475 
3476  //---------------------------------------
3477  // write cells
3478  out << "object \"cells\" class array type int rank 1 shape "
3479  << GeometryInfo<dim>::vertices_per_cell << " items " << n_cells;
3480 
3481  if (flags.int_binary)
3482  {
3483  out << " lsb binary data " << offset << '\n';
3484  offset += n_cells * sizeof(int);
3485  }
3486  else
3487  {
3488  out << " data follows" << '\n';
3489  write_cells(patches, dx_out);
3490  out << '\n';
3491  }
3492 
3493 
3494  out << "attribute \"element type\" string \"";
3495  if (dim == 1)
3496  out << "lines";
3497  if (dim == 2)
3498  out << "quads";
3499  if (dim == 3)
3500  out << "cubes";
3501  out << "\"" << '\n' << "attribute \"ref\" string \"positions\"" << '\n';
3502 
3503  // TODO:[GK] Patches must be of same size!
3504  //---------------------------
3505  // write neighbor information
3506  if (flags.write_neighbors)
3507  {
3508  out << "object \"neighbors\" class array type int rank 1 shape "
3509  << GeometryInfo<dim>::faces_per_cell << " items " << n_cells
3510  << " data follows";
3511 
3512  for (const auto &patch : patches)
3513  {
3514  const unsigned int n = patch.n_subdivisions;
3515  const unsigned int n1 = (dim > 0) ? n : 1;
3516  const unsigned int n2 = (dim > 1) ? n : 1;
3517  const unsigned int n3 = (dim > 2) ? n : 1;
3518  unsigned int cells_per_patch = Utilities::fixed_power<dim>(n);
3519  unsigned int dx = 1;
3520  unsigned int dy = n;
3521  unsigned int dz = n * n;
3522 
3523  const unsigned int patch_start =
3524  patch.patch_index * cells_per_patch;
3525 
3526  for (unsigned int i3 = 0; i3 < n3; ++i3)
3527  for (unsigned int i2 = 0; i2 < n2; ++i2)
3528  for (unsigned int i1 = 0; i1 < n1; ++i1)
3529  {
3530  const unsigned int nx = i1 * dx;
3531  const unsigned int ny = i2 * dy;
3532  const unsigned int nz = i3 * dz;
3533 
3534  // There are no neighbors for dim==0. Note that this case is
3535  // caught by the AssertThrow at the beginning of this
3536  // function anyway. This condition avoids compiler warnings.
3537  if (dim < 1)
3538  continue;
3539 
3540  out << '\n';
3541  // Direction -x Last cell in row of other patch
3542  if (i1 == 0)
3543  {
3544  const unsigned int nn = patch.neighbors[0];
3545  out << '\t';
3546  if (nn != patch.no_neighbor)
3547  out
3548  << (nn * cells_per_patch + ny + nz + dx * (n - 1));
3549  else
3550  out << "-1";
3551  }
3552  else
3553  {
3554  out << '\t' << patch_start + nx - dx + ny + nz;
3555  }
3556  // Direction +x First cell in row of other patch
3557  if (i1 == n - 1)
3558  {
3559  const unsigned int nn = patch.neighbors[1];
3560  out << '\t';
3561  if (nn != patch.no_neighbor)
3562  out << (nn * cells_per_patch + ny + nz);
3563  else
3564  out << "-1";
3565  }
3566  else
3567  {
3568  out << '\t' << patch_start + nx + dx + ny + nz;
3569  }
3570  if (dim < 2)
3571  continue;
3572  // Direction -y
3573  if (i2 == 0)
3574  {
3575  const unsigned int nn = patch.neighbors[2];
3576  out << '\t';
3577  if (nn != patch.no_neighbor)
3578  out
3579  << (nn * cells_per_patch + nx + nz + dy * (n - 1));
3580  else
3581  out << "-1";
3582  }
3583  else
3584  {
3585  out << '\t' << patch_start + nx + ny - dy + nz;
3586  }
3587  // Direction +y
3588  if (i2 == n - 1)
3589  {
3590  const unsigned int nn = patch.neighbors[3];
3591  out << '\t';
3592  if (nn != patch.no_neighbor)
3593  out << (nn * cells_per_patch + nx + nz);
3594  else
3595  out << "-1";
3596  }
3597  else
3598  {
3599  out << '\t' << patch_start + nx + ny + dy + nz;
3600  }
3601  if (dim < 3)
3602  continue;
3603 
3604  // Direction -z
3605  if (i3 == 0)
3606  {
3607  const unsigned int nn = patch.neighbors[4];
3608  out << '\t';
3609  if (nn != patch.no_neighbor)
3610  out
3611  << (nn * cells_per_patch + nx + ny + dz * (n - 1));
3612  else
3613  out << "-1";
3614  }
3615  else
3616  {
3617  out << '\t' << patch_start + nx + ny + nz - dz;
3618  }
3619  // Direction +z
3620  if (i3 == n - 1)
3621  {
3622  const unsigned int nn = patch.neighbors[5];
3623  out << '\t';
3624  if (nn != patch.no_neighbor)
3625  out << (nn * cells_per_patch + nx + ny);
3626  else
3627  out << "-1";
3628  }
3629  else
3630  {
3631  out << '\t' << patch_start + nx + ny + nz + dz;
3632  }
3633  }
3634  out << '\n';
3635  }
3636  }
3637  //---------------------------
3638  // now write data
3639  if (n_data_sets != 0)
3640  {
3641  out << "object \"data\" class array type float rank 1 shape "
3642  << n_data_sets << " items " << n_nodes;
3643 
3644  if (flags.data_binary)
3645  {
3646  out << " lsb ieee data " << offset << '\n';
3647  offset += n_data_sets * n_nodes *
3648  ((flags.data_double) ? sizeof(double) : sizeof(float));
3649  }
3650  else
3651  {
3652  out << " data follows" << '\n';
3653  write_data(patches, n_data_sets, flags.data_double, dx_out);
3654  }
3655 
3656  // loop over all patches
3657  out << "attribute \"dep\" string \"positions\"" << '\n';
3658  }
3659  else
3660  {
3661  out << "object \"data\" class constantarray type float rank 0 items "
3662  << n_nodes << " data follows" << '\n'
3663  << '0' << '\n';
3664  }
3665 
3666  // no model data
3667 
3668  out << "object \"deal data\" class field" << '\n'
3669  << "component \"positions\" value \"vertices\"" << '\n'
3670  << "component \"connections\" value \"cells\"" << '\n'
3671  << "component \"data\" value \"data\"" << '\n';
3672 
3673  if (flags.write_neighbors)
3674  out << "component \"neighbors\" value \"neighbors\"" << '\n';
3675 
3676  {
3677  out << "attribute \"created\" string \"" << Utilities::System::get_date()
3678  << ' ' << Utilities::System::get_time() << '"' << '\n';
3679  }
3680 
3681  out << "end" << '\n';
3682  // Write all binary data now
3683  if (flags.coordinates_binary)
3684  write_nodes(patches, dx_out);
3685  if (flags.int_binary)
3686  write_cells(patches, dx_out);
3687  if (flags.data_binary)
3688  write_data(patches, n_data_sets, flags.data_double, dx_out);
3689 
3690  // make sure everything now gets to disk
3691  out.flush();
3692 
3693  // assert the stream is still ok
3694  AssertThrow(out.fail() == false, ExcIO());
3695  }
3696 
3697 
3698 
3699  template <int dim, int spacedim>
3700  void
3702  const std::vector<Patch<dim, spacedim>> &patches,
3703  const std::vector<std::string> & data_names,
3704  const std::vector<
3705  std::tuple<unsigned int,
3706  unsigned int,
3707  std::string,
3709  const GnuplotFlags &flags,
3710  std::ostream & out)
3711  {
3712  AssertThrow(out.fail() == false, ExcIO());
3713 
3714 #ifndef DEAL_II_WITH_MPI
3715  // verify that there are indeed patches to be written out. most
3716  // of the times, people just forget to call build_patches when there
3717  // are no patches, so a warning is in order. that said, the
3718  // assertion is disabled if we support MPI since then it can
3719  // happen that on the coarsest mesh, a processor simply has no
3720  // cells it actually owns, and in that case it is legit if there
3721  // are no patches
3722  Assert(patches.size() > 0, ExcNoPatches());
3723 #else
3724  if (patches.size() == 0)
3725  return;
3726 #endif
3727 
3728  const unsigned int n_data_sets = data_names.size();
3729 
3730  // write preamble
3731  {
3732  out << "# This file was generated by the deal.II library." << '\n'
3733  << "# Date = " << Utilities::System::get_date() << '\n'
3734  << "# Time = " << Utilities::System::get_time() << '\n'
3735  << "#" << '\n'
3736  << "# For a description of the GNUPLOT format see the GNUPLOT manual."
3737  << '\n'
3738  << "#" << '\n'
3739  << "# ";
3740 
3741  AssertThrow(spacedim <= flags.space_dimension_labels.size(),
3743  for (unsigned int spacedim_n = 0; spacedim_n < spacedim; ++spacedim_n)
3744  {
3745  out << '<' << flags.space_dimension_labels.at(spacedim_n) << "> ";
3746  }
3747 
3748  for (const auto &data_name : data_names)
3749  out << '<' << data_name << "> ";
3750  out << '\n';
3751  }
3752 
3753 
3754  // loop over all patches
3755  for (const auto &patch : patches)
3756  {
3757  const unsigned int n_subdivisions = patch.n_subdivisions;
3758  const unsigned int n_points_per_direction = n_subdivisions + 1;
3759 
3760  Assert((patch.data.n_rows() == n_data_sets &&
3761  !patch.points_are_available) ||
3762  (patch.data.n_rows() == n_data_sets + spacedim &&
3763  patch.points_are_available),
3765  (n_data_sets + spacedim) :
3766  n_data_sets,
3767  patch.data.n_rows()));
3768 
3769  auto output_point_data =
3770  [&out, &patch, n_data_sets](const unsigned int point_index) mutable {
3771  for (unsigned int data_set = 0; data_set < n_data_sets; ++data_set)
3772  out << patch.data(data_set, point_index) << ' ';
3773  };
3774 
3775  switch (dim)
3776  {
3777  case 0:
3778  {
3779  Assert(patch.reference_cell == ReferenceCells::Vertex,
3780  ExcInternalError());
3781  Assert(patch.data.n_cols() == 1,
3782  ExcInvalidDatasetSize(patch.data.n_cols(),
3783  n_subdivisions + 1));
3784 
3785 
3786  // compute coordinates for this patch point
3787  out << get_equispaced_location(patch, {}, n_subdivisions)
3788  << ' ';
3789  output_point_data(0);
3790  out << '\n';
3791  out << '\n';
3792  break;
3793  }
3794 
3795  case 1:
3796  {
3797  Assert(patch.reference_cell == ReferenceCells::Line,
3798  ExcInternalError());
3799  Assert(patch.data.n_cols() ==
3800  Utilities::fixed_power<dim>(n_points_per_direction),
3801  ExcInvalidDatasetSize(patch.data.n_cols(),
3802  n_subdivisions + 1));
3803 
3804  for (unsigned int i1 = 0; i1 < n_points_per_direction; ++i1)
3805  {
3806  // compute coordinates for this patch point
3807  out << get_equispaced_location(patch, {i1}, n_subdivisions)
3808  << ' ';
3809 
3810  output_point_data(i1);
3811  out << '\n';
3812  }
3813  // end of patch
3814  out << '\n';
3815  out << '\n';
3816  break;
3817  }
3818 
3819  case 2:
3820  {
3821  if (patch.reference_cell == ReferenceCells::Quadrilateral)
3822  {
3823  Assert(patch.data.n_cols() == Utilities::fixed_power<dim>(
3824  n_points_per_direction),
3825  ExcInvalidDatasetSize(patch.data.n_cols(),
3826  n_subdivisions + 1));
3827 
3828  for (unsigned int i2 = 0; i2 < n_points_per_direction; ++i2)
3829  {
3830  for (unsigned int i1 = 0; i1 < n_points_per_direction;
3831  ++i1)
3832  {
3833  // compute coordinates for this patch point
3834  out << get_equispaced_location(patch,
3835  {i1, i2},
3836  n_subdivisions)
3837  << ' ';
3838 
3839  output_point_data(i1 + i2 * n_points_per_direction);
3840  out << '\n';
3841  }
3842  // end of row in patch
3843  out << '\n';
3844  }
3845  }
3846  else if (patch.reference_cell == ReferenceCells::Triangle)
3847  {
3848  Assert(n_subdivisions == 1, ExcNotImplemented());
3849 
3850  Assert(patch.data.n_cols() == 3, ExcInternalError());
3851 
3852  // Gnuplot can only plot surfaces if each facet of the
3853  // surface is a bilinear patch, or a subdivided bilinear
3854  // patch with equally many points along each row of the
3855  // subdivision. This is what the code above for
3856  // quadrilaterals does. We emulate this by repeating the
3857  // third point of a triangle twice so that there are two
3858  // points for that row as well -- i.e., we write a 2x2
3859  // bilinear patch where two of the points are collapsed onto
3860  // one vertex.
3861  //
3862  // This also matches the example here:
3863  // https://stackoverflow.com/questions/42784369/drawing-triangular-mesh-using-gnuplot
3864  out << get_node_location(patch, 0) << ' ';
3865  output_point_data(0);
3866  out << '\n';
3867 
3868  out << get_node_location(patch, 1) << ' ';
3869  output_point_data(1);
3870  out << '\n';
3871  out << '\n'; // end of one row of points
3872 
3873  out << get_node_location(patch, 2) << ' ';
3874  output_point_data(2);
3875  out << '\n';
3876 
3877  out << get_node_location(patch, 2) << ' ';
3878  output_point_data(2);
3879  out << '\n';
3880  out << '\n'; // end of the second row of points
3881  out << '\n'; // end of the entire patch
3882  }
3883  else
3884  // There aren't any other reference cells in 2d than the
3885  // quadrilateral and the triangle. So whatever we got here
3886  // can't be any good
3887  Assert(false, ExcInternalError());
3888  // end of patch
3889  out << '\n';
3890 
3891  break;
3892  }
3893 
3894  case 3:
3895  {
3896  if (patch.reference_cell == ReferenceCells::Hexahedron)
3897  {
3898  Assert(patch.data.n_cols() == Utilities::fixed_power<dim>(
3899  n_points_per_direction),
3900  ExcInvalidDatasetSize(patch.data.n_cols(),
3901  n_subdivisions + 1));
3902 
3903  // for all grid points: draw lines into all positive
3904  // coordinate directions if there is another grid point
3905  // there
3906  for (unsigned int i3 = 0; i3 < n_points_per_direction; ++i3)
3907  for (unsigned int i2 = 0; i2 < n_points_per_direction;
3908  ++i2)
3909  for (unsigned int i1 = 0; i1 < n_points_per_direction;
3910  ++i1)
3911  {
3912  // compute coordinates for this patch point
3913  const Point<spacedim> this_point =
3914  get_equispaced_location(patch,
3915  {i1, i2, i3},
3916  n_subdivisions);
3917  // line into positive x-direction if possible
3918  if (i1 < n_subdivisions)
3919  {
3920  // write point here and its data
3921  out << this_point << ' ';
3922  output_point_data(i1 +
3923  i2 * n_points_per_direction +
3924  i3 * n_points_per_direction *
3925  n_points_per_direction);
3926  out << '\n';
3927 
3928  // write point there and its data
3929  out << get_equispaced_location(patch,
3930  {i1 + 1, i2, i3},
3931  n_subdivisions)
3932  << ' ';
3933 
3934  output_point_data((i1 + 1) +
3935  i2 * n_points_per_direction +
3936  i3 * n_points_per_direction *
3937  n_points_per_direction);
3938  out << '\n';
3939 
3940  // end of line
3941  out << '\n' << '\n';
3942  }
3943 
3944  // line into positive y-direction if possible
3945  if (i2 < n_subdivisions)
3946  {
3947  // write point here and its data
3948  out << this_point << ' ';
3949  output_point_data(i1 +
3950  i2 * n_points_per_direction +
3951  i3 * n_points_per_direction *
3952  n_points_per_direction);
3953  out << '\n';
3954 
3955  // write point there and its data
3956  out << get_equispaced_location(patch,
3957  {i1, i2 + 1, i3},
3958  n_subdivisions)
3959  << ' ';
3960 
3961  output_point_data(
3962  i1 + (i2 + 1) * n_points_per_direction +
3963  i3 * n_points_per_direction *
3964  n_points_per_direction);
3965  out << '\n';
3966 
3967  // end of line
3968  out << '\n' << '\n';
3969  }
3970 
3971  // line into positive z-direction if possible
3972  if (i3 < n_subdivisions)
3973  {
3974  // write point here and its data
3975  out << this_point << ' ';
3976  output_point_data(i1 +
3977  i2 * n_points_per_direction +
3978  i3 * n_points_per_direction *
3979  n_points_per_direction);
3980  out << '\n';
3981 
3982  // write point there and its data
3983  out << get_equispaced_location(patch,
3984  {i1, i2, i3 + 1},
3985  n_subdivisions)
3986  << ' ';
3987 
3988  output_point_data(
3989  i1 + i2 * n_points_per_direction +
3990  (i3 + 1) * n_points_per_direction *
3991  n_points_per_direction);
3992  out << '\n';
3993  // end of line
3994  out << '\n' << '\n';
3995  }
3996  }
3997  }
3998  else if (patch.reference_cell == ReferenceCells::Tetrahedron)
3999  {
4000  Assert(n_subdivisions == 1, ExcNotImplemented());
4001 
4002  // Draw the tetrahedron as a collection of two lines.
4003  for (const unsigned int v : {0, 1, 2, 0, 3, 2})
4004  {
4005  out << get_node_location(patch, v) << ' ';
4006  output_point_data(v);
4007  out << '\n';
4008  }
4009  out << '\n'; // end of first line
4010 
4011  for (const unsigned int v : {3, 1})
4012  {
4013  out << get_node_location(patch, v) << ' ';
4014  output_point_data(v);
4015  out << '\n';
4016  }
4017  out << '\n'; // end of second line
4018  }
4019  else if (patch.reference_cell == ReferenceCells::Pyramid)
4020  {
4021  Assert(n_subdivisions == 1, ExcNotImplemented());
4022 
4023  // Draw the pyramid as a collection of two lines.
4024  for (const unsigned int v : {0, 1, 3, 2, 0, 4, 1})
4025  {
4026  out << get_node_location(patch, v) << ' ';
4027  output_point_data(v);
4028  out << '\n';
4029  }
4030  out << '\n'; // end of first line
4031 
4032  for (const unsigned int v : {2, 4, 3})
4033  {
4034  out << get_node_location(patch, v) << ' ';
4035  output_point_data(v);
4036  out << '\n';
4037  }
4038  out << '\n'; // end of second line
4039  }
4040  else if (patch.reference_cell == ReferenceCells::Wedge)
4041  {
4042  Assert(n_subdivisions == 1, ExcNotImplemented());
4043 
4044  // Draw the wedge as a collection of three
4045  // lines. The first one wraps around the base,
4046  // goes up to the top, and wraps around that. The
4047  // second and third are just individual lines
4048  // going from base to top.
4049  for (const unsigned int v : {0, 1, 2, 0, 3, 4, 5, 3})
4050  {
4051  out << get_node_location(patch, v) << ' ';
4052  output_point_data(v);
4053  out << '\n';
4054  }
4055  out << '\n'; // end of first line
4056 
4057  for (const unsigned int v : {1, 4})
4058  {
4059  out << get_node_location(patch, v) << ' ';
4060  output_point_data(v);
4061  out << '\n';
4062  }
4063  out << '\n'; // end of second line
4064 
4065  for (const unsigned int v : {2, 5})
4066  {
4067  out << get_node_location(patch, v) << ' ';
4068  output_point_data(v);
4069  out << '\n';
4070  }
4071  out << '\n'; // end of second line
4072  }
4073  else
4074  // No other reference cells are currently implemented
4075  Assert(false, ExcNotImplemented());
4076 
4077  break;
4078  }
4079 
4080  default:
4081  Assert(false, ExcNotImplemented());
4082  }
4083  }
4084  // make sure everything now gets to disk
4085  out.flush();
4086 
4087  AssertThrow(out.fail() == false, ExcIO());
4088  }
4089 
4090 
4091  namespace
4092  {
4093  template <int dim, int spacedim>
4094  void
4095  do_write_povray(const std::vector<Patch<dim, spacedim>> &,
4096  const std::vector<std::string> &,
4097  const PovrayFlags &,
4098  std::ostream &)
4099  {
4100  Assert(false,
4101  ExcMessage("Writing files in POVRAY format is only supported "
4102  "for two-dimensional meshes."));
4103  }
4104 
4105 
4106 
4107  void
4108  do_write_povray(const std::vector<Patch<2, 2>> &patches,
4109  const std::vector<std::string> &data_names,
4110  const PovrayFlags & flags,
4111  std::ostream & out)
4112  {
4113  AssertThrow(out.fail() == false, ExcIO());
4114 
4115 #ifndef DEAL_II_WITH_MPI
4116  // verify that there are indeed patches to be written out. most
4117  // of the times, people just forget to call build_patches when there
4118  // are no patches, so a warning is in order. that said, the
4119  // assertion is disabled if we support MPI since then it can
4120  // happen that on the coarsest mesh, a processor simply has no cells it
4121  // actually owns, and in that case it is legit if there are no patches
4122  Assert(patches.size() > 0, ExcNoPatches());
4123 #else
4124  if (patches.size() == 0)
4125  return;
4126 #endif
4127  constexpr int dim = 2;
4128  (void)dim;
4129  constexpr int spacedim = 2;
4130 
4131  const unsigned int n_data_sets = data_names.size();
4132  (void)n_data_sets;
4133 
4134  // write preamble
4135  {
4136  out
4137  << "/* This file was generated by the deal.II library." << '\n'
4138  << " Date = " << Utilities::System::get_date() << '\n'
4139  << " Time = " << Utilities::System::get_time() << '\n'
4140  << '\n'
4141  << " For a description of the POVRAY format see the POVRAY manual."
4142  << '\n'
4143  << "*/ " << '\n';
4144 
4145  // include files
4146  out << "#include \"colors.inc\" " << '\n'
4147  << "#include \"textures.inc\" " << '\n';
4148 
4149 
4150  // use external include file for textures, camera and light
4151  if (flags.external_data)
4152  out << "#include \"data.inc\" " << '\n';
4153  else // all definitions in data file
4154  {
4155  // camera
4156  out << '\n'
4157  << '\n'
4158  << "camera {" << '\n'
4159  << " location <1,4,-7>" << '\n'
4160  << " look_at <0,0,0>" << '\n'
4161  << " angle 30" << '\n'
4162  << "}" << '\n';
4163 
4164  // light
4165  out << '\n'
4166  << "light_source {" << '\n'
4167  << " <1,4,-7>" << '\n'
4168  << " color Grey" << '\n'
4169  << "}" << '\n';
4170  out << '\n'
4171  << "light_source {" << '\n'
4172  << " <0,20,0>" << '\n'
4173  << " color White" << '\n'
4174  << "}" << '\n';
4175  }
4176  }
4177 
4178  // max. and min. height of solution
4179  Assert(patches.size() > 0, ExcNoPatches());
4180  double hmin = patches[0].data(0, 0);
4181  double hmax = patches[0].data(0, 0);
4182 
4183  for (const auto &patch : patches)
4184  {
4185  const unsigned int n_subdivisions = patch.n_subdivisions;
4186 
4187  Assert((patch.data.n_rows() == n_data_sets &&
4188  !patch.points_are_available) ||
4189  (patch.data.n_rows() == n_data_sets + spacedim &&
4190  patch.points_are_available),
4192  (n_data_sets + spacedim) :
4193  n_data_sets,
4194  patch.data.n_rows()));
4195  Assert(patch.data.n_cols() ==
4196  Utilities::fixed_power<dim>(n_subdivisions + 1),
4197  ExcInvalidDatasetSize(patch.data.n_cols(),
4198  n_subdivisions + 1));
4199 
4200  for (unsigned int i = 0; i < n_subdivisions + 1; ++i)
4201  for (unsigned int j = 0; j < n_subdivisions + 1; ++j)
4202  {
4203  const int dl = i * (n_subdivisions + 1) + j;
4204  if (patch.data(0, dl) < hmin)
4205  hmin = patch.data(0, dl);
4206  if (patch.data(0, dl) > hmax)
4207  hmax = patch.data(0, dl);
4208  }
4209  }
4210 
4211  out << "#declare HMIN=" << hmin << ";" << '\n'
4212  << "#declare HMAX=" << hmax << ";" << '\n'
4213  << '\n';
4214 
4215  if (!flags.external_data)
4216  {
4217  // texture with scaled niveau lines 10 lines in the surface
4218  out << "#declare Tex=texture{" << '\n'
4219  << " pigment {" << '\n'
4220  << " gradient y" << '\n'
4221  << " scale y*(HMAX-HMIN)*" << 0.1 << '\n'
4222  << " color_map {" << '\n'
4223  << " [0.00 color Light_Purple] " << '\n'
4224  << " [0.95 color Light_Purple] " << '\n'
4225  << " [1.00 color White] " << '\n'
4226  << "} } }" << '\n'
4227  << '\n';
4228  }
4229 
4230  if (!flags.bicubic_patch)
4231  {
4232  // start of mesh header
4233  out << '\n' << "mesh {" << '\n';
4234  }
4235 
4236  // loop over all patches
4237  for (const auto &patch : patches)
4238  {
4239  const unsigned int n_subdivisions = patch.n_subdivisions;
4240  const unsigned int n = n_subdivisions + 1;
4241  const unsigned int d1 = 1;
4242  const unsigned int d2 = n;
4243 
4244  Assert((patch.data.n_rows() == n_data_sets &&
4245  !patch.points_are_available) ||
4246  (patch.data.n_rows() == n_data_sets + spacedim &&
4247  patch.points_are_available),
4249  (n_data_sets + spacedim) :
4250  n_data_sets,
4251  patch.data.n_rows()));
4252  Assert(patch.data.n_cols() == Utilities::fixed_power<dim>(n),
4253  ExcInvalidDatasetSize(patch.data.n_cols(),
4254  n_subdivisions + 1));
4255 
4256 
4257  std::vector<Point<spacedim>> ver(n * n);
4258 
4259  for (unsigned int i2 = 0; i2 < n; ++i2)
4260  for (unsigned int i1 = 0; i1 < n; ++i1)
4261  {
4262  // compute coordinates for this patch point, storing in ver
4263  ver[i1 * d1 + i2 * d2] =
4264  get_equispaced_location(patch, {i1, i2}, n_subdivisions);
4265  }
4266 
4267 
4268  if (!flags.bicubic_patch)
4269  {
4270  // approximate normal vectors in patch
4271  std::vector<Point<3>> nrml;
4272  // only if smooth triangles are used
4273  if (flags.smooth)
4274  {
4275  nrml.resize(n * n);
4276  // These are difference quotients of the surface
4277  // mapping. We take them symmetric inside the
4278  // patch and one-sided at the edges
4279  Point<3> h1, h2;
4280  // Now compute normals in every point
4281  for (unsigned int i = 0; i < n; ++i)
4282  for (unsigned int j = 0; j < n; ++j)
4283  {
4284  const unsigned int il = (i == 0) ? i : (i - 1);
4285  const unsigned int ir =
4286  (i == n_subdivisions) ? i : (i + 1);
4287  const unsigned int jl = (j == 0) ? j : (j - 1);
4288  const unsigned int jr =
4289  (j == n_subdivisions) ? j : (j + 1);
4290 
4291  h1(0) =
4292  ver[ir * d1 + j * d2](0) - ver[il * d1 + j * d2](0);
4293  h1(1) = patch.data(0, ir * d1 + j * d2) -
4294  patch.data(0, il * d1 + j * d2);
4295  h1(2) =
4296  ver[ir * d1 + j * d2](1) - ver[il * d1 + j * d2](1);
4297 
4298  h2(0) =
4299  ver[i * d1 + jr * d2](0) - ver[i * d1 + jl * d2](0);
4300  h2(1) = patch.data(0, i * d1 + jr * d2) -
4301  patch.data(0, i * d1 + jl * d2);
4302  h2(2) =
4303  ver[i * d1 + jr * d2](1) - ver[i * d1 + jl * d2](1);
4304 
4305  nrml[i * d1 + j * d2](0) =
4306  h1(1) * h2(2) - h1(2) * h2(1);
4307  nrml[i * d1 + j * d2](1) =
4308  h1(2) * h2(0) - h1(0) * h2(2);
4309  nrml[i * d1 + j * d2](2) =
4310  h1(0) * h2(1) - h1(1) * h2(0);
4311 
4312  // normalize Vector
4313  double norm =
4314  std::sqrt(std::pow(nrml[i * d1 + j * d2](0), 2.) +
4315  std::pow(nrml[i * d1 + j * d2](1), 2.) +
4316  std::pow(nrml[i * d1 + j * d2](2), 2.));
4317 
4318  if (nrml[i * d1 + j * d2](1) < 0)
4319  norm *= -1.;
4320 
4321  for (unsigned int k = 0; k < 3; ++k)
4322  nrml[i * d1 + j * d2](k) /= norm;
4323  }
4324  }
4325 
4326  // setting up triangles
4327  for (unsigned int i = 0; i < n_subdivisions; ++i)
4328  for (unsigned int j = 0; j < n_subdivisions; ++j)
4329  {
4330  // down/left vertex of triangle
4331  const int dl = i * d1 + j * d2;
4332  if (flags.smooth)
4333  {
4334  // writing smooth_triangles
4335 
4336  // down/right triangle
4337  out << "smooth_triangle {" << '\n'
4338  << "\t<" << ver[dl](0) << "," << patch.data(0, dl)
4339  << "," << ver[dl](1) << ">, <" << nrml[dl](0)
4340  << ", " << nrml[dl](1) << ", " << nrml[dl](2)
4341  << ">," << '\n';
4342  out << " \t<" << ver[dl + d1](0) << ","
4343  << patch.data(0, dl + d1) << "," << ver[dl + d1](1)
4344  << ">, <" << nrml[dl + d1](0) << ", "
4345  << nrml[dl + d1](1) << ", " << nrml[dl + d1](2)
4346  << ">," << '\n';
4347  out << "\t<" << ver[dl + d1 + d2](0) << ","
4348  << patch.data(0, dl + d1 + d2) << ","
4349  << ver[dl + d1 + d2](1) << ">, <"
4350  << nrml[dl + d1 + d2](0) << ", "
4351  << nrml[dl + d1 + d2](1) << ", "
4352  << nrml[dl + d1 + d2](2) << ">}" << '\n';
4353 
4354  // upper/left triangle
4355  out << "smooth_triangle {" << '\n'
4356  << "\t<" << ver[dl](0) << "," << patch.data(0, dl)
4357  << "," << ver[dl](1) << ">, <" << nrml[dl](0)
4358  << ", " << nrml[dl](1) << ", " << nrml[dl](2)
4359  << ">," << '\n';
4360  out << "\t<" << ver[dl + d1 + d2](0) << ","
4361  << patch.data(0, dl + d1 + d2) << ","
4362  << ver[dl + d1 + d2](1) << ">, <"
4363  << nrml[dl + d1 + d2](0) << ", "
4364  << nrml[dl + d1 + d2](1) << ", "
4365  << nrml[dl + d1 + d2](2) << ">," << '\n';
4366  out << "\t<" << ver[dl + d2](0) << ","
4367  << patch.data(0, dl + d2) << "," << ver[dl + d2](1)
4368  << ">, <" << nrml[dl + d2](0) << ", "
4369  << nrml[dl + d2](1) << ", " << nrml[dl + d2](2)
4370  << ">}" << '\n';
4371  }
4372  else
4373  {
4374  // writing standard triangles down/right triangle
4375  out << "triangle {" << '\n'
4376  << "\t<" << ver[dl](0) << "," << patch.data(0, dl)
4377  << "," << ver[dl](1) << ">," << '\n';
4378  out << "\t<" << ver[dl + d1](0) << ","
4379  << patch.data(0, dl + d1) << "," << ver[dl + d1](1)
4380  << ">," << '\n';
4381  out << "\t<" << ver[dl + d1 + d2](0) << ","
4382  << patch.data(0, dl + d1 + d2) << ","
4383  << ver[dl + d1 + d2](1) << ">}" << '\n';
4384 
4385  // upper/left triangle
4386  out << "triangle {" << '\n'
4387  << "\t<" << ver[dl](0) << "," << patch.data(0, dl)
4388  << "," << ver[dl](1) << ">," << '\n';
4389  out << "\t<" << ver[dl + d1 + d2](0) << ","
4390  << patch.data(0, dl + d1 + d2) << ","
4391  << ver[dl + d1 + d2](1) << ">," << '\n';
4392  out << "\t<" << ver[dl + d2](0) << ","
4393  << patch.data(0, dl + d2) << "," << ver[dl + d2](1)
4394  << ">}" << '\n';
4395  }
4396  }
4397  }
4398  else
4399  {
4400  // writing bicubic_patch
4401  Assert(n_subdivisions == 3,
4402  ExcDimensionMismatch(n_subdivisions, 3));
4403  out << '\n'
4404  << "bicubic_patch {" << '\n'
4405  << " type 0" << '\n'
4406  << " flatness 0" << '\n'
4407  << " u_steps 0" << '\n'
4408  << " v_steps 0" << '\n';
4409  for (int i = 0; i < 16; ++i)
4410  {
4411  out << "\t<" << ver[i](0) << "," << patch.data(0, i) << ","
4412  << ver[i](1) << ">";
4413  if (i != 15)
4414  out << ",";
4415  out << '\n';
4416  }
4417  out << " texture {Tex}" << '\n' << "}" << '\n';
4418  }
4419  }
4420 
4421  if (!flags.bicubic_patch)
4422  {
4423  // the end of the mesh
4424  out << " texture {Tex}" << '\n' << "}" << '\n' << '\n';
4425  }
4426 
4427  // make sure everything now gets to disk
4428  out.flush();
4429 
4430  AssertThrow(out.fail() == false, ExcIO());
4431  }
4432  } // namespace
4433 
4434 
4435 
4436  template <int dim, int spacedim>
4437  void
4439  const std::vector<Patch<dim, spacedim>> &patches,
4440  const std::vector<std::string> & data_names,
4441  const std::vector<
4442  std::tuple<unsigned int,
4443  unsigned int,
4444  std::string,
4446  const PovrayFlags &flags,
4447  std::ostream & out)
4448  {
4449  do_write_povray(patches, data_names, flags, out);
4450  }
4451 
4452 
4453 
4454  template <int dim, int spacedim>
4455  void
4457  const std::vector<Patch<dim, spacedim>> & /*patches*/,
4458  const std::vector<std::string> & /*data_names*/,
4459  const std::vector<
4460  std::tuple<unsigned int,
4461  unsigned int,
4462  std::string,
4464  const EpsFlags & /*flags*/,
4465  std::ostream & /*out*/)
4466  {
4467  // not implemented, see the documentation of the function
4468  AssertThrow(dim == 2, ExcNotImplemented());
4469  }
4470 
4471 
4472  template <int spacedim>
4473  void
4475  const std::vector<Patch<2, spacedim>> &patches,
4476  const std::vector<std::string> & /*data_names*/,
4477  const std::vector<
4478  std::tuple<unsigned int,
4479  unsigned int,
4480  std::string,
4482  const EpsFlags &flags,
4483  std::ostream & out)
4484  {
4485  AssertThrow(out.fail() == false, ExcIO());
4486 
4487 #ifndef DEAL_II_WITH_MPI
4488  // verify that there are indeed patches to be written out. most of the
4489  // times, people just forget to call build_patches when there are no
4490  // patches, so a warning is in order. that said, the assertion is disabled
4491  // if we support MPI since then it can happen that on the coarsest mesh, a
4492  // processor simply has no cells it actually owns, and in that case it is
4493  // legit if there are no patches
4494  Assert(patches.size() > 0, ExcNoPatches());
4495 #else
4496  if (patches.size() == 0)
4497  return;
4498 #endif
4499 
4500  // set up an array of cells to be written later. this array holds the cells
4501  // of all the patches as projected to the plane perpendicular to the line of
4502  // sight.
4503  //
4504  // note that they are kept sorted by the set, where we chose the value of
4505  // the center point of the cell along the line of sight as value for sorting
4506  std::multiset<EpsCell2d> cells;
4507 
4508  // two variables in which we will store the minimum and maximum values of
4509  // the field to be used for colorization
4510  float min_color_value = std::numeric_limits<float>::max();
4511  float max_color_value = std::numeric_limits<float>::min();
4512 
4513  // Array for z-coordinates of points. The elevation determined by a function
4514  // if spacedim=2 or the z-cooridate of the grid point if spacedim=3
4515  double heights[4] = {0, 0, 0, 0};
4516 
4517  // compute the cells for output and enter them into the set above note that
4518  // since dim==2, we have exactly four vertices per patch and per cell
4519  for (const auto &patch : patches)
4520  {
4521  const unsigned int n_subdivisions = patch.n_subdivisions;
4522  const unsigned int n = n_subdivisions + 1;
4523  const unsigned int d1 = 1;
4524  const unsigned int d2 = n;
4525 
4526  for (unsigned int i2 = 0; i2 < n_subdivisions; ++i2)
4527  for (unsigned int i1 = 0; i1 < n_subdivisions; ++i1)
4528  {
4529  Point<spacedim> points[4];
4530  points[0] =
4531  get_equispaced_location(patch, {i1, i2}, n_subdivisions);
4532  points[1] =
4533  get_equispaced_location(patch, {i1 + 1, i2}, n_subdivisions);
4534  points[2] =
4535  get_equispaced_location(patch, {i1, i2 + 1}, n_subdivisions);
4536  points[3] = get_equispaced_location(patch,
4537  {i1 + 1, i2 + 1},
4538  n_subdivisions);
4539 
4540  switch (spacedim)
4541  {
4542  case 2:
4543  Assert((flags.height_vector < patch.data.n_rows()) ||
4544  patch.data.n_rows() == 0,
4546  0,
4547  patch.data.n_rows()));
4548  heights[0] =
4549  patch.data.n_rows() != 0 ?
4550  patch.data(flags.height_vector, i1 * d1 + i2 * d2) *
4551  flags.z_scaling :
4552  0;
4553  heights[1] = patch.data.n_rows() != 0 ?
4554  patch.data(flags.height_vector,
4555  (i1 + 1) * d1 + i2 * d2) *
4556  flags.z_scaling :
4557  0;
4558  heights[2] = patch.data.n_rows() != 0 ?
4559  patch.data(flags.height_vector,
4560  i1 * d1 + (i2 + 1) * d2) *
4561  flags.z_scaling :
4562  0;
4563  heights[3] = patch.data.n_rows() != 0 ?
4564  patch.data(flags.height_vector,
4565  (i1 + 1) * d1 + (i2 + 1) * d2) *
4566  flags.z_scaling :
4567  0;
4568 
4569  break;
4570  case 3:
4571  // Copy z-coordinates into the height vector
4572  for (unsigned int i = 0; i < 4; ++i)
4573  heights[i] = points[i](2);
4574  break;
4575  default:
4576  Assert(false, ExcNotImplemented());
4577  }
4578 
4579 
4580  // now compute the projection of the bilinear cell given by the
4581  // four vertices and their heights and write them to a proper cell
4582  // object. note that we only need the first two components of the
4583  // projected position for output, but we need the value along the
4584  // line of sight for sorting the cells for back-to- front-output
4585  //
4586  // this computation was first written by Stefan Nauber. please
4587  // no-one ask me why it works that way (or may be not), especially
4588  // not about the angles and the sign of the height field, I don't
4589  // know it.
4590  EpsCell2d eps_cell;
4591  const double pi = numbers::PI;
4592  const double cx =
4593  -std::cos(pi - flags.azimut_angle * 2 * pi / 360.),
4594  cz = -std::cos(flags.turn_angle * 2 * pi / 360.),
4595  sx =
4596  std::sin(pi - flags.azimut_angle * 2 * pi / 360.),
4597  sz = std::sin(flags.turn_angle * 2 * pi / 360.);
4598  for (unsigned int vertex = 0; vertex < 4; ++vertex)
4599  {
4600  const double x = points[vertex](0), y = points[vertex](1),
4601  z = -heights[vertex];
4602 
4603  eps_cell.vertices[vertex](0) = -cz * x + sz * y;
4604  eps_cell.vertices[vertex](1) =
4605  -cx * sz * x - cx * cz * y - sx * z;
4606 
4607  // ( 1 0 0 )
4608  // D1 = ( 0 cx -sx )
4609  // ( 0 sx cx )
4610 
4611  // ( cy 0 sy )
4612  // Dy = ( 0 1 0 )
4613  // (-sy 0 cy )
4614 
4615  // ( cz -sz 0 )
4616  // Dz = ( sz cz 0 )
4617  // ( 0 0 1 )
4618 
4619  // ( cz -sz 0 )( 1 0 0 )(x) (
4620  // cz*x-sz*(cx*y-sx*z)+0*(sx*y+cx*z) )
4621  // Dxz = ( sz cz 0 )( 0 cx -sx )(y) = (
4622  // sz*x+cz*(cx*y-sx*z)+0*(sx*y+cx*z) )
4623  // ( 0 0 1 )( 0 sx cx )(z) ( 0*x+
4624  // *(cx*y-sx*z)+1*(sx*y+cx*z) )
4625  }
4626 
4627  // compute coordinates of center of cell
4628  const Point<spacedim> center_point =
4629  (points[0] + points[1] + points[2] + points[3]) / 4;
4630  const double center_height =
4631  -(heights[0] + heights[1] + heights[2] + heights[3]) / 4;
4632 
4633  // compute the depth into the picture
4634  eps_cell.depth = -sx * sz * center_point(0) -
4635  sx * cz * center_point(1) + cx * center_height;
4636 
4637  if (flags.draw_cells && flags.shade_cells)
4638  {
4639  Assert((flags.color_vector < patch.data.n_rows()) ||
4640  patch.data.n_rows() == 0,
4642  0,
4643  patch.data.n_rows()));
4644  const double color_values[4] = {
4645  patch.data.n_rows() != 0 ?
4646  patch.data(flags.color_vector, i1 * d1 + i2 * d2) :
4647  1,
4648 
4649  patch.data.n_rows() != 0 ?
4650  patch.data(flags.color_vector, (i1 + 1) * d1 + i2 * d2) :
4651  1,
4652 
4653  patch.data.n_rows() != 0 ?
4654  patch.data(flags.color_vector, i1 * d1 + (i2 + 1) * d2) :
4655  1,
4656 
4657  patch.data.n_rows() != 0 ?
4658  patch.data(flags.color_vector,
4659  (i1 + 1) * d1 + (i2 + 1) * d2) :
4660  1};
4661 
4662  // set color value to average of the value at the vertices
4663  eps_cell.color_value = (color_values[0] + color_values[1] +
4664  color_values[3] + color_values[2]) /
4665  4;
4666 
4667  // update bounds of color field
4668  min_color_value =
4669  std::min(min_color_value, eps_cell.color_value);
4670  max_color_value =
4671  std::max(max_color_value, eps_cell.color_value);
4672  }
4673 
4674  // finally add this cell
4675  cells.insert(eps_cell);
4676  }
4677  }
4678 
4679  // find out minimum and maximum x and y coordinates to compute offsets and
4680  // scaling factors
4681  double x_min = cells.begin()->vertices[0](0);
4682  double x_max = x_min;
4683  double y_min = cells.begin()->vertices[0](1);
4684  double y_max = y_min;
4685 
4686  for (const auto &cell : cells)
4687  for (const auto &vertex : cell.vertices)
4688  {
4689  x_min = std::min(x_min, vertex(0));
4690  x_max = std::max(x_max, vertex(0));
4691  y_min = std::min(y_min, vertex(1));
4692  y_max = std::max(y_max, vertex(1));
4693  }
4694 
4695  // scale in x-direction such that in the output 0 <= x <= 300. don't scale
4696  // in y-direction to preserve the shape of the triangulation
4697  const double scale =
4698  (flags.size /
4699  (flags.size_type == EpsFlags::width ? x_max - x_min : y_min - y_max));
4700 
4701  const Point<2> offset(x_min, y_min);
4702 
4703 
4704  // now write preamble
4705  {
4706  out << "%!PS-Adobe-2.0 EPSF-1.2" << '\n'
4707  << "%%Title: deal.II Output" << '\n'
4708  << "%%Creator: the deal.II library" << '\n'
4709  << "%%Creation Date: " << Utilities::System::get_date() << " - "
4710  << Utilities::System::get_time() << '\n'
4711  << "%%BoundingBox: "
4712  // lower left corner
4713  << "0 0 "
4714  // upper right corner
4715  << static_cast<unsigned int>((x_max - x_min) * scale + 0.5) << ' '
4716  << static_cast<unsigned int>((y_max - y_min) * scale + 0.5) << '\n';
4717 
4718  // define some abbreviations to keep the output small:
4719  // m=move turtle to
4720  // l=define a line
4721  // s=set rgb color
4722  // sg=set gray value
4723  // lx=close the line and plot the line
4724  // lf=close the line and fill the interior
4725  out << "/m {moveto} bind def" << '\n'
4726  << "/l {lineto} bind def" << '\n'
4727  << "/s {setrgbcolor} bind def" << '\n'
4728  << "/sg {setgray} bind def" << '\n'
4729  << "/lx {lineto closepath stroke} bind def" << '\n'
4730  << "/lf {lineto closepath fill} bind def" << '\n';
4731 
4732  out << "%%EndProlog" << '\n' << '\n';
4733  // set fine lines
4734  out << flags.line_width << " setlinewidth" << '\n';
4735  }
4736 
4737  // check if min and max values for the color are actually different. If
4738  // that is not the case (such things happen, for example, in the very first
4739  // time step of a time dependent problem, if the initial values are zero),
4740  // all values are equal, and then we can draw everything in an arbitrary
4741  // color. Thus, change one of the two values arbitrarily
4742  if (max_color_value == min_color_value)
4743  max_color_value = min_color_value + 1;
4744 
4745  // now we've got all the information we need. write the cells. note: due to
4746  // the ordering, we traverse the list of cells back-to-front
4747  for (const auto &cell : cells)
4748  {
4749  if (flags.draw_cells)
4750  {
4751  if (flags.shade_cells)
4752  {
4753  const EpsFlags::RgbValues rgb_values =
4754  (*flags.color_function)(cell.color_value,
4755  min_color_value,
4756  max_color_value);
4757 
4758  // write out color
4759  if (rgb_values.is_grey())
4760  out << rgb_values.red << " sg ";
4761  else
4762  out << rgb_values.red << ' ' << rgb_values.green << ' '
4763  << rgb_values.blue << " s ";
4764  }
4765  else
4766  out << "1 sg ";
4767 
4768  out << (cell.vertices[0] - offset) * scale << " m "
4769  << (cell.vertices[1] - offset) * scale << " l "
4770  << (cell.vertices[3] - offset) * scale << " l "
4771  << (cell.vertices[2] - offset) * scale << " lf" << '\n';
4772  }
4773 
4774  if (flags.draw_mesh)
4775  out << "0 sg " // draw lines in black
4776  << (cell.vertices[0] - offset) * scale << " m "
4777  << (cell.vertices[1] - offset) * scale << " l "
4778  << (cell.vertices[3] - offset) * scale << " l "
4779  << (cell.vertices[2] - offset) * scale << " lx" << '\n';
4780  }
4781  out << "showpage" << '\n';
4782 
4783  out.flush();
4784 
4785  AssertThrow(out.fail() == false, ExcIO());
4786  }
4787 
4788 
4789 
4790  template <int dim, int spacedim>
4791  void
4793  const std::vector<Patch<dim, spacedim>> &patches,
4794  const std::vector<std::string> & data_names,
4795  const std::vector<
4796  std::tuple<unsigned int,
4797  unsigned int,
4798  std::string,
4800  const GmvFlags &flags,
4801  std::ostream & out)
4802  {
4803  // The gmv format does not support cells that only consist of a single
4804  // point. It does support the output of point data using the keyword
4805  // 'tracers' instead of 'nodes' and 'cells', but this output format is
4806  // currently not implemented.
4807  AssertThrow(dim > 0, ExcNotImplemented());
4808 
4809  Assert(dim <= 3, ExcNotImplemented());
4810  AssertThrow(out.fail() == false, ExcIO());
4811 
4812 #ifndef DEAL_II_WITH_MPI
4813  // verify that there are indeed patches to be written out. most of the
4814  // times, people just forget to call build_patches when there are no
4815  // patches, so a warning is in order. that said, the assertion is disabled
4816  // if we support MPI since then it can happen that on the coarsest mesh, a
4817  // processor simply has no cells it actually owns, and in that case it is
4818  // legit if there are no patches
4819  Assert(patches.size() > 0, ExcNoPatches());
4820 #else
4821  if (patches.size() == 0)
4822  return;
4823 #endif
4824 
4825  GmvStream gmv_out(out, flags);
4826  const unsigned int n_data_sets = data_names.size();
4827  // check against # of data sets in first patch. checks against all other
4828  // patches are made in write_gmv_reorder_data_vectors
4829  Assert((patches[0].data.n_rows() == n_data_sets &&
4830  !patches[0].points_are_available) ||
4831  (patches[0].data.n_rows() == n_data_sets + spacedim &&
4832  patches[0].points_are_available),
4833  ExcDimensionMismatch(patches[0].points_are_available ?
4834  (n_data_sets + spacedim) :
4835  n_data_sets,
4836  patches[0].data.n_rows()));
4837 
4838  //---------------------
4839  // preamble
4840  out << "gmvinput ascii" << '\n' << '\n';
4841 
4842  // first count the number of cells and cells for later use
4843  unsigned int n_nodes;
4844  unsigned int n_cells;
4845  compute_sizes<dim, spacedim>(patches, n_nodes, n_cells);
4846 
4847  // in gmv format the vertex coordinates and the data have an order that is a
4848  // bit unpleasant (first all x coordinates, then all y coordinate, ...;
4849  // first all data of variable 1, then variable 2, etc), so we have to copy
4850  // the data vectors a bit around
4851  //
4852  // note that we copy vectors when looping over the patches since we have to
4853  // write them one variable at a time and don't want to use more than one
4854  // loop
4855  //
4856  // this copying of data vectors can be done while we already output the
4857  // vertices, so do this on a separate task and when wanting to write out the
4858  // data, we wait for that task to finish
4859  Table<2, double> data_vectors(n_data_sets, n_nodes);
4860  void (*fun_ptr)(const std::vector<Patch<dim, spacedim>> &,
4861  Table<2, double> &) =
4862  &write_gmv_reorder_data_vectors<dim, spacedim>;
4863  Threads::Task<> reorder_task =
4864  Threads::new_task(fun_ptr, patches, data_vectors);
4865 
4866  //-----------------------------
4867  // first make up a list of used vertices along with their coordinates
4868  //
4869  // note that we have to print 3 dimensions
4870  out << "nodes " << n_nodes << '\n';
4871  for (unsigned int d = 0; d < spacedim; ++d)
4872  {
4873  gmv_out.selected_component = d;
4874  write_nodes(patches, gmv_out);
4875  out << '\n';
4876  }
4877  gmv_out.selected_component = numbers::invalid_unsigned_int;
4878 
4879  for (unsigned int d = spacedim; d < 3; ++d)
4880  {
4881  for (unsigned int i = 0; i < n_nodes; ++i)
4882  out << "0 ";
4883  out << '\n';
4884  }
4885 
4886  //-------------------------------
4887  // now for the cells. note that vertices are counted from 1 onwards
4888  out << "cells " << n_cells << '\n';
4889  write_cells(patches, gmv_out);
4890 
4891  //-------------------------------------
4892  // data output.
4893  out << "variable" << '\n';
4894 
4895  // now write the data vectors to @p{out} first make sure that all data is in
4896  // place
4897  reorder_task.join();
4898 
4899  // then write data. the '1' means: node data (as opposed to cell data, which
4900  // we do not support explicitly here)
4901  for (unsigned int data_set = 0; data_set < n_data_sets; ++data_set)
4902  {
4903  out << data_names[data_set] << " 1" << '\n';
4904  std::copy(data_vectors[data_set].begin(),
4905  data_vectors[data_set].end(),
4906  std::ostream_iterator<double>(out, " "));
4907  out << '\n' << '\n';
4908  }
4909 
4910 
4911 
4912  // end of variable section
4913  out << "endvars" << '\n';
4914 
4915  // end of output
4916  out << "endgmv" << '\n';
4917 
4918  // make sure everything now gets to disk
4919  out.flush();
4920 
4921  // assert the stream is still ok
4922  AssertThrow(out.fail() == false, ExcIO());
4923  }
4924 
4925 
4926 
4927  template <int dim, int spacedim>
4928  void
4930  const std::vector<Patch<dim, spacedim>> &patches,
4931  const std::vector<std::string> & data_names,
4932  const std::vector<
4933  std::tuple<unsigned int,
4934  unsigned int,
4935  std::string,
4937  const TecplotFlags &flags,
4938  std::ostream & out)
4939  {
4940  AssertThrow(out.fail() == false, ExcIO());
4941 
4942  // The FEBLOCK or FEPOINT formats of tecplot only allows full elements (e.g.
4943  // triangles), not single points. Other tecplot format allow point output,
4944  // but they are currently not implemented.
4945  AssertThrow(dim > 0, ExcNotImplemented());
4946 
4947 #ifndef DEAL_II_WITH_MPI
4948  // verify that there are indeed patches to be written out. most of the
4949  // times, people just forget to call build_patches when there are no
4950  // patches, so a warning is in order. that said, the assertion is disabled
4951  // if we support MPI since then it can happen that on the coarsest mesh, a
4952  // processor simply has no cells it actually owns, and in that case it is
4953  // legit if there are no patches
4954  Assert(patches.size() > 0, ExcNoPatches());
4955 #else
4956  if (patches.size() == 0)
4957  return;
4958 #endif
4959 
4960  TecplotStream tecplot_out(out, flags);
4961 
4962  const unsigned int n_data_sets = data_names.size();
4963  // check against # of data sets in first patch. checks against all other
4964  // patches are made in write_gmv_reorder_data_vectors
4965  Assert((patches[0].data.n_rows() == n_data_sets &&
4966  !patches[0].points_are_available) ||
4967  (patches[0].data.n_rows() == n_data_sets + spacedim &&
4968  patches[0].points_are_available),
4969  ExcDimensionMismatch(patches[0].points_are_available ?
4970  (n_data_sets + spacedim) :
4971  n_data_sets,
4972  patches[0].data.n_rows()));
4973 
4974  // first count the number of cells and cells for later use
4975  unsigned int n_nodes;
4976  unsigned int n_cells;
4977  compute_sizes<dim, spacedim>(patches, n_nodes, n_cells);
4978 
4979  //---------
4980  // preamble
4981  {
4982  out
4983  << "# This file was generated by the deal.II library." << '\n'
4984  << "# Date = " << Utilities::System::get_date() << '\n'
4985  << "# Time = " << Utilities::System::get_time() << '\n'
4986  << "#" << '\n'
4987  << "# For a description of the Tecplot format see the Tecplot documentation."
4988  << '\n'
4989  << "#" << '\n';
4990 
4991 
4992  out << "Variables=";
4993 
4994  switch (spacedim)
4995  {
4996  case 1:
4997  out << "\"x\"";
4998  break;
4999  case 2:
5000  out << "\"x\", \"y\"";
5001  break;
5002  case 3:
5003  out << "\"x\", \"y\", \"z\"";
5004  break;
5005  default:
5006  Assert(false, ExcNotImplemented());
5007  }
5008 
5009  for (unsigned int data_set = 0; data_set < n_data_sets; ++data_set)
5010  out << ", \"" << data_names[data_set] << "\"";
5011 
5012  out << '\n';
5013 
5014  out << "zone ";
5015  if (flags.zone_name)
5016  out << "t=\"" << flags.zone_name << "\" ";
5017 
5018  if (flags.solution_time >= 0.0)
5019  out << "strandid=1, solutiontime=" << flags.solution_time << ", ";
5020 
5021  out << "f=feblock, n=" << n_nodes << ", e=" << n_cells
5022  << ", et=" << tecplot_cell_type[dim] << '\n';
5023  }
5024 
5025 
5026  // in Tecplot FEBLOCK format the vertex coordinates and the data have an
5027  // order that is a bit unpleasant (first all x coordinates, then all y
5028  // coordinate, ...; first all data of variable 1, then variable 2, etc), so
5029  // we have to copy the data vectors a bit around
5030  //
5031  // note that we copy vectors when looping over the patches since we have to
5032  // write them one variable at a time and don't want to use more than one
5033  // loop
5034  //
5035  // this copying of data vectors can be done while we already output the
5036  // vertices, so do this on a separate task and when wanting to write out the
5037  // data, we wait for that task to finish
5038 
5039  Table<2, double> data_vectors(n_data_sets, n_nodes);
5040 
5041  void (*fun_ptr)(const std::vector<Patch<dim, spacedim>> &,
5042  Table<2, double> &) =
5043  &write_gmv_reorder_data_vectors<dim, spacedim>;
5044  Threads::Task<> reorder_task =
5045  Threads::new_task(fun_ptr, patches, data_vectors);
5046 
5047  //-----------------------------
5048  // first make up a list of used vertices along with their coordinates
5049 
5050 
5051  for (unsigned int d = 0; d < spacedim; ++d)
5052  {
5053  tecplot_out.selected_component = d;
5054  write_nodes(patches, tecplot_out);
5055  out << '\n';
5056  }
5057 
5058 
5059  //-------------------------------------
5060  // data output.
5061  //
5062  // now write the data vectors to @p{out} first make sure that all data is in
5063  // place
5064  reorder_task.join();
5065 
5066  // then write data.
5067  for (unsigned int data_set = 0; data_set < n_data_sets; ++data_set)
5068  {
5069  std::copy(data_vectors[data_set].begin(),
5070  data_vectors[data_set].end(),
5071  std::ostream_iterator<double>(out, "\n"));
5072  out << '\n';
5073  }
5074 
5075  write_cells(patches, tecplot_out);
5076 
5077  // make sure everything now gets to disk
5078  out.flush();
5079 
5080  // assert the stream is still ok
5081  AssertThrow(out.fail() == false, ExcIO());
5082  }
5083 
5084 
5085 
5086  //---------------------------------------------------------------------------
5087  // Macros for handling Tecplot API data
5088 
5089 #ifdef DEAL_II_HAVE_TECPLOT
5090 
5091  namespace
5092  {
5093  class TecplotMacros
5094  {
5095  public:
5096  TecplotMacros(const unsigned int n_nodes = 0,
5097  const unsigned int n_vars = 0,
5098  const unsigned int n_cells = 0,
5099  const unsigned int n_vert = 0);
5100  ~TecplotMacros();
5101  float &
5102  nd(const unsigned int i, const unsigned int j);
5103  int &
5104  cd(const unsigned int i, const unsigned int j);
5105  std::vector<float> nodalData;
5106  std::vector<int> connData;
5107 
5108  private:
5109  unsigned int n_nodes;
5110  unsigned int n_vars;
5111  unsigned int n_cells;
5112  unsigned int n_vert;
5113  };
5114 
5115 
5116  inline TecplotMacros::TecplotMacros(const unsigned int n_nodes,
5117  const unsigned int n_vars,
5118  const unsigned int n_cells,
5119  const unsigned int n_vert)
5120  : n_nodes(n_nodes)
5121  , n_vars(n_vars)
5122  , n_cells(n_cells)
5123  , n_vert(n_vert)
5124  {
5125  nodalData.resize(n_nodes * n_vars);
5126  connData.resize(n_cells * n_vert);
5127  }
5128 
5129 
5130 
5131  inline TecplotMacros::~TecplotMacros()
5132  {}
5133 
5134 
5135 
5136  inline float &
5137  TecplotMacros::nd(const unsigned int i, const unsigned int j)
5138  {
5139  return nodalData[i * n_nodes + j];
5140  }
5141 
5142 
5143 
5144  inline int &
5145  TecplotMacros::cd(const unsigned int i, const unsigned int j)
5146  {
5147  return connData[i + j * n_vert];
5148  }
5149 
5150  } // namespace
5151 
5152 
5153 #endif
5154  //---------------------------------------------------------------------------
5155 
5156 
5157 
5158  template <int dim, int spacedim>
5159  void
5161  const std::vector<Patch<dim, spacedim>> &patches,
5162  const std::vector<std::string> & data_names,
5163  const std::vector<
5164  std::tuple<unsigned int,
5165  unsigned int,
5166  std::string,
5168  & nonscalar_data_ranges,
5169  const TecplotFlags &flags,
5170  std::ostream & out)
5171  {
5172  // The FEBLOCK or FEPOINT formats of tecplot only allows full elements (e.g.
5173  // triangles), not single points. Other tecplot format allow point output,
5174  // but they are currently not implemented.
5175  AssertThrow(dim > 0, ExcNotImplemented());
5176 
5177 #ifndef DEAL_II_HAVE_TECPLOT
5178 
5179  // simply call the ASCII output function if the Tecplot API isn't present
5180  write_tecplot(patches, data_names, nonscalar_data_ranges, flags, out);
5181  return;
5182 
5183 #else
5184 
5185  // Tecplot binary output only good for 2D & 3D
5186  if (dim == 1)
5187  {
5188  write_tecplot(patches, data_names, nonscalar_data_ranges, flags, out);
5189  return;
5190  }
5191 
5192  // if the user hasn't specified a file name we should call the ASCII
5193  // function and use the ostream @p{out} instead of doing something silly
5194  // later
5195  char *file_name = (char *)flags.tecplot_binary_file_name;
5196 
5197  if (file_name == nullptr)
5198  {
5199  // At least in debug mode we should tell users why they don't get
5200  // tecplot binary output
5201  Assert(false,
5202  ExcMessage("Specify the name of the tecplot_binary"
5203  " file through the TecplotFlags interface."));
5204  write_tecplot(patches, data_names, nonscalar_data_ranges, flags, out);
5205  return;
5206  }
5207 
5208 
5209  AssertThrow(out.fail() == false, ExcIO());
5210 
5211 # ifndef DEAL_II_WITH_MPI
5212  // verify that there are indeed patches to be written out. most of the
5213  // times, people just forget to call build_patches when there are no
5214  // patches, so a warning is in order. that said, the assertion is disabled
5215  // if we support MPI since then it can happen that on the coarsest mesh, a
5216  // processor simply has no cells it actually owns, and in that case it is
5217  // legit if there are no patches
5218  Assert(patches.size() > 0, ExcNoPatches());
5219 # else
5220  if (patches.size() == 0)
5221  return;
5222 # endif
5223 
5224  const unsigned int n_data_sets = data_names.size();
5225  // check against # of data sets in first patch. checks against all other
5226  // patches are made in write_gmv_reorder_data_vectors
5227  Assert((patches[0].data.n_rows() == n_data_sets &&
5228  !patches[0].points_are_available) ||
5229  (patches[0].data.n_rows() == n_data_sets + spacedim &&
5230  patches[0].points_are_available),
5231  ExcDimensionMismatch(patches[0].points_are_available ?
5232  (n_data_sets + spacedim) :
5233  n_data_sets,
5234  patches[0].data.n_rows()));
5235 
5236  // first count the number of cells and cells for later use
5237  unsigned int n_nodes;
5238  unsigned int n_cells;
5239  compute_sizes<dim, spacedim>(patches, n_nodes, n_cells);
5240  // local variables only needed to write Tecplot binary output files
5241  const unsigned int vars_per_node = (spacedim + n_data_sets),
5242  nodes_per_cell = GeometryInfo<dim>::vertices_per_cell;
5243 
5244  TecplotMacros tm(n_nodes, vars_per_node, n_cells, nodes_per_cell);
5245 
5246  int is_double = 0, tec_debug = 0, cell_type = tecplot_binary_cell_type[dim];
5247 
5248  std::string tec_var_names;
5249  switch (spacedim)
5250  {
5251  case 2:
5252  tec_var_names = "x y";
5253  break;
5254  case 3:
5255  tec_var_names = "x y z";
5256  break;
5257  default:
5258  Assert(false, ExcNotImplemented());
5259  }
5260 
5261  for (unsigned int data_set = 0; data_set < n_data_sets; ++data_set)
5262  {
5263  tec_var_names += " ";
5264  tec_var_names += data_names[data_set];
5265  }
5266  // in Tecplot FEBLOCK format the vertex coordinates and the data have an
5267  // order that is a bit unpleasant (first all x coordinates, then all y
5268  // coordinate, ...; first all data of variable 1, then variable 2, etc), so
5269  // we have to copy the data vectors a bit around
5270  //
5271  // note that we copy vectors when looping over the patches since we have to
5272  // write them one variable at a time and don't want to use more than one
5273  // loop
5274  //
5275  // this copying of data vectors can be done while we already output the
5276  // vertices, so do this on a separate task and when wanting to write out the
5277  // data, we wait for that task to finish
5278  Table<2, double> data_vectors(n_data_sets, n_nodes);
5279 
5280  void (*fun_ptr)(const std::vector<Patch<dim, spacedim>> &,
5281  Table<2, double> &) =
5282  &write_gmv_reorder_data_vectors<dim, spacedim>;
5283  Threads::Task<> reorder_task =
5284  Threads::new_task(fun_ptr, patches, data_vectors);
5285 
5286  //-----------------------------
5287  // first make up a list of used vertices along with their coordinates
5288  for (unsigned int d = 1; d <= spacedim; ++d)
5289  {
5290  unsigned int entry = 0;
5291 
5292  for (const auto &patch : patches)
5293  {
5294  const unsigned int n_subdivisions = patch.n_subdivisions;
5295 
5296  switch (dim)
5297  {
5298  case 2:
5299  {
5300  for (unsigned int j = 0; j < n_subdivisions + 1; ++j)
5301  for (unsigned int i = 0; i < n_subdivisions + 1; ++i)
5302  {
5303  const double x_frac = i * 1. / n_subdivisions,
5304  y_frac = j * 1. / n_subdivisions;
5305 
5306  tm.nd((d - 1), entry) = static_cast<float>(
5307  (((patch.vertices[1](d - 1) * x_frac) +
5308  (patch.vertices[0](d - 1) * (1 - x_frac))) *
5309  (1 - y_frac) +
5310  ((patch.vertices[3](d - 1) * x_frac) +
5311  (patch.vertices[2](d - 1) * (1 - x_frac))) *
5312  y_frac));
5313  entry++;
5314  }
5315  break;
5316  }
5317 
5318  case 3:
5319  {
5320  for (unsigned int j = 0; j < n_subdivisions + 1; ++j)
5321  for (unsigned int k = 0; k < n_subdivisions + 1; ++k)
5322  for (unsigned int i = 0; i < n_subdivisions + 1; ++i)
5323  {
5324  const double x_frac = i * 1. / n_subdivisions,
5325  y_frac = k * 1. / n_subdivisions,
5326  z_frac = j * 1. / n_subdivisions;
5327 
5328  // compute coordinates for this patch point
5329  tm.nd((d - 1), entry) = static_cast<float>(
5330  ((((patch.vertices[1](d - 1) * x_frac) +
5331  (patch.vertices[0](d - 1) * (1 - x_frac))) *
5332  (1 - y_frac) +
5333  ((patch.vertices[3](d - 1) * x_frac) +
5334  (patch.vertices[2](d - 1) * (1 - x_frac))) *
5335  y_frac) *
5336  (1 - z_frac) +
5337  (((patch.vertices[5](d - 1) * x_frac) +
5338  (patch.vertices[4](d - 1) * (1 - x_frac))) *
5339  (1 - y_frac) +
5340  ((patch.vertices[7](d - 1) * x_frac) +
5341  (patch.vertices[6](d - 1) * (1 - x_frac))) *
5342  y_frac) *
5343  z_frac));
5344  entry++;
5345  }
5346  break;
5347  }
5348 
5349  default:
5350  Assert(false, ExcNotImplemented());
5351  }
5352  }
5353  }
5354 
5355 
5356  //-------------------------------------
5357  // data output.
5358  //
5359  reorder_task.join();
5360 
5361  // then write data.
5362  for (unsigned int data_set = 0; data_set < n_data_sets; ++data_set)
5363  for (unsigned int entry = 0; entry < data_vectors[data_set].size();
5364  entry++)
5365  tm.nd((spacedim + data_set), entry) =
5366  static_cast<float>(data_vectors[data_set][entry]);
5367 
5368 
5369 
5370  //-------------------------------
5371  // now for the cells. note that vertices are counted from 1 onwards
5372  unsigned int first_vertex_of_patch = 0;
5373  unsigned int elem = 0;
5374 
5375  for (const auto &patch : patches)
5376  {
5377  const unsigned int n_subdivisions = patch.n_subdivisions;
5378  const unsigned int n = n_subdivisions + 1;
5379  const unsigned int d1 = 1;
5380  const unsigned int d2 = n;
5381  const unsigned int d3 = n * n;
5382  // write out the cells making up this patch
5383  switch (dim)
5384  {
5385  case 2:
5386  {
5387  for (unsigned int i2 = 0; i2 < n_subdivisions; ++i2)
5388  for (unsigned int i1 = 0; i1 < n_subdivisions; ++i1)
5389  {
5390  tm.cd(0, elem) =
5391  first_vertex_of_patch + (i1)*d1 + (i2)*d2 + 1;
5392  tm.cd(1, elem) =
5393  first_vertex_of_patch + (i1 + 1) * d1 + (i2)*d2 + 1;
5394  tm.cd(2, elem) = first_vertex_of_patch + (i1 + 1) * d1 +
5395  (i2 + 1) * d2 + 1;
5396  tm.cd(3, elem) =
5397  first_vertex_of_patch + (i1)*d1 + (i2 + 1) * d2 + 1;
5398 
5399  elem++;
5400  }
5401  break;
5402  }
5403 
5404  case 3:
5405  {
5406  for (unsigned int i3 = 0; i3 < n_subdivisions; ++i3)
5407  for (unsigned int i2 = 0; i2 < n_subdivisions; ++i2)
5408  for (unsigned int i1 = 0; i1 < n_subdivisions; ++i1)
5409  {
5410  // note: vertex indices start with 1!
5411 
5412 
5413  tm.cd(0, elem) = first_vertex_of_patch + (i1)*d1 +
5414  (i2)*d2 + (i3)*d3 + 1;
5415  tm.cd(1, elem) = first_vertex_of_patch + (i1 + 1) * d1 +
5416  (i2)*d2 + (i3)*d3 + 1;
5417  tm.cd(2, elem) = first_vertex_of_patch + (i1 + 1) * d1 +
5418  (i2 + 1) * d2 + (i3)*d3 + 1;
5419  tm.cd(3, elem) = first_vertex_of_patch + (i1)*d1 +
5420  (i2 + 1) * d2 + (i3)*d3 + 1;
5421  tm.cd(4, elem) = first_vertex_of_patch + (i1)*d1 +
5422  (i2)*d2 + (i3 + 1) * d3 + 1;
5423  tm.cd(5, elem) = first_vertex_of_patch + (i1 + 1) * d1 +
5424  (i2)*d2 + (i3 + 1) * d3 + 1;
5425  tm.cd(6, elem) = first_vertex_of_patch + (i1 + 1) * d1 +
5426  (i2 + 1) * d2 + (i3 + 1) * d3 + 1;
5427  tm.cd(7, elem) = first_vertex_of_patch + (i1)*d1 +
5428  (i2 + 1) * d2 + (i3 + 1) * d3 + 1;
5429 
5430  elem++;
5431  }
5432  break;
5433  }
5434 
5435  default:
5436  Assert(false, ExcNotImplemented());
5437  }
5438 
5439 
5440  // finally update the number of the first vertex of this patch
5441  first_vertex_of_patch += Utilities::fixed_power<dim>(n);
5442  }
5443 
5444 
5445  {
5446  int ierr = 0, num_nodes = static_cast<int>(n_nodes),
5447  num_cells = static_cast<int>(n_cells);
5448 
5449  char dot[2] = {'.', 0};
5450  // Unfortunately, TECINI takes a char *, but c_str() gives a const char *.
5451  // As we don't do anything else with tec_var_names following const_cast is
5452  // ok
5453  char *var_names = const_cast<char *>(tec_var_names.c_str());
5454  ierr = TECINI(nullptr, var_names, file_name, dot, &tec_debug, &is_double);
5455 
5456  Assert(ierr == 0, ExcErrorOpeningTecplotFile(file_name));
5457 
5458  char FEBLOCK[] = {'F', 'E', 'B', 'L', 'O', 'C', 'K', 0};
5459  ierr =
5460  TECZNE(nullptr, &num_nodes, &num_cells, &cell_type, FEBLOCK, nullptr);
5461 
5462  Assert(ierr == 0, ExcTecplotAPIError());
5463 
5464  int total = (vars_per_node * num_nodes);
5465 
5466  ierr = TECDAT(&total, tm.nodalData.data(), &is_double);
5467 
5468  Assert(ierr == 0, ExcTecplotAPIError());
5469 
5470  ierr = TECNOD(tm.connData.data());
5471 
5472  Assert(ierr == 0, ExcTecplotAPIError());
5473 
5474  ierr = TECEND();
5475 
5476  Assert(ierr == 0, ExcTecplotAPIError());
5477  }
5478 #endif
5479  }
5480 
5481 
5482 
5483  template <int dim, int spacedim>
5484  void
5486  const std::vector<Patch<dim, spacedim>> &patches,
5487  const std::vector<std::string> & data_names,
5488  const std::vector<
5489  std::tuple<unsigned int,
5490  unsigned int,
5491  std::string,
5493  & nonscalar_data_ranges,
5494  const VtkFlags &flags,
5495  std::ostream & out)
5496  {
5497  AssertThrow(out.fail() == false, ExcIO());
5498 
5499 #ifndef DEAL_II_WITH_MPI
5500  // verify that there are indeed patches to be written out. most of the
5501  // times, people just forget to call build_patches when there are no
5502  // patches, so a warning is in order. that said, the assertion is disabled
5503  // if we support MPI since then it can happen that on the coarsest mesh, a
5504  // processor simply has no cells it actually owns, and in that case it is
5505  // legit if there are no patches
5506  Assert(patches.size() > 0, ExcNoPatches());
5507 #else
5508  if (patches.size() == 0)
5509  return;
5510 #endif
5511 
5512  VtkStream vtk_out(out, flags);
5513 
5514  const unsigned int n_data_sets = data_names.size();
5515  // check against # of data sets in first patch.
5516  if (patches[0].points_are_available)
5517  {
5518  AssertDimension(n_data_sets + spacedim, patches[0].data.n_rows())
5519  }
5520  else
5521  {
5522  AssertDimension(n_data_sets, patches[0].data.n_rows())
5523  }
5524 
5525  //---------------------
5526  // preamble
5527  {
5528  out << "# vtk DataFile Version 3.0" << '\n'
5529  << "#This file was generated by the deal.II library";
5530  if (flags.print_date_and_time)
5531  {
5532  out << " on " << Utilities::System::get_date() << " at "
5534  }
5535  else
5536  out << '.';
5537  out << '\n' << "ASCII" << '\n';
5538  // now output the data header
5539  out << "DATASET UNSTRUCTURED_GRID\n" << '\n';
5540  }
5541 
5542  // if desired, output time and cycle of the simulation, following the
5543  // instructions at
5544  // http://www.visitusers.org/index.php?title=Time_and_Cycle_in_VTK_files
5545  {
5546  const unsigned int n_metadata =
5547  ((flags.cycle != std::numeric_limits<unsigned int>::min() ? 1 : 0) +
5548  (flags.time != std::numeric_limits<double>::min() ? 1 : 0));
5549  if (n_metadata > 0)
5550  {
5551  out << "FIELD FieldData " << n_metadata << '\n';
5552 
5554  {
5555  out << "CYCLE 1 1 int\n" << flags.cycle << '\n';
5556  }
5557  if (flags.time != std::numeric_limits<double>::min())
5558  {
5559  out << "TIME 1 1 double\n" << flags.time << '\n';
5560  }
5561  }
5562  }
5563 
5564  // first count the number of cells and cells for later use
5565  unsigned int n_nodes, n_cells, n_points_and_n_cells;
5566  compute_sizes(patches,
5568  n_nodes,
5569  n_cells,
5570  n_points_and_n_cells);
5571 
5572  // in gmv format the vertex coordinates and the data have an order that is a
5573  // bit unpleasant (first all x coordinates, then all y coordinate, ...;
5574  // first all data of variable 1, then variable 2, etc), so we have to copy
5575  // the data vectors a bit around
5576  //
5577  // note that we copy vectors when looping over the patches since we have to
5578  // write them one variable at a time and don't want to use more than one
5579  // loop
5580  //
5581  // this copying of data vectors can be done while we already output the
5582  // vertices, so do this on a separate task and when wanting to write out the
5583  // data, we wait for that task to finish
5584  Table<2, double> data_vectors(n_data_sets, n_nodes);
5585 
5586  void (*fun_ptr)(const std::vector<Patch<dim, spacedim>> &,
5587  Table<2, double> &) =
5588  &write_gmv_reorder_data_vectors<dim, spacedim>;
5589  Threads::Task<> reorder_task =
5590  Threads::new_task(fun_ptr, patches, data_vectors);
5591 
5592  //-----------------------------
5593  // first make up a list of used vertices along with their coordinates
5594  //
5595  // note that we have to print d=1..3 dimensions
5596  out << "POINTS " << n_nodes << " double" << '\n';
5597  write_nodes(patches, vtk_out);
5598  out << '\n';
5599  //-------------------------------
5600  // now for the cells
5601  out << "CELLS " << n_cells << ' ' << n_points_and_n_cells << '\n';
5602  if (flags.write_higher_order_cells)
5603  write_high_order_cells(patches, vtk_out);
5604  else
5605  write_cells(patches, vtk_out);
5606  out << '\n';
5607  // next output the types of the cells. since all cells are the same, this is
5608  // simple
5609  out << "CELL_TYPES " << n_cells << '\n';
5610 
5611  // need to distinguish between linear cells, simplex cells (linear or
5612  // quadratic), and high order cells
5613  for (const auto &patch : patches)
5614  {
5615  const auto vtk_cell_id =
5616  extract_vtk_patch_info(patch, flags.write_higher_order_cells);
5617 
5618  for (unsigned int i = 0; i < vtk_cell_id[1]; ++i)
5619  out << ' ' << vtk_cell_id[0];
5620  }
5621 
5622  out << '\n';
5623  //-------------------------------------
5624  // data output.
5625 
5626  // now write the data vectors to @p{out} first make sure that all data is in
5627  // place
5628  reorder_task.join();
5629 
5630  // then write data. the 'POINT_DATA' means: node data (as opposed to cell
5631  // data, which we do not support explicitly here). all following data sets
5632  // are point data
5633  out << "POINT_DATA " << n_nodes << '\n';
5634 
5635  // when writing, first write out all vector data, then handle the scalar
5636  // data sets that have been left over
5637  std::vector<bool> data_set_written(n_data_sets, false);
5638  for (const auto &nonscalar_data_range : nonscalar_data_ranges)
5639  {
5640  AssertThrow(std::get<3>(nonscalar_data_range) !=
5642  ExcNotImplemented());
5643 
5644  AssertThrow(std::get<1>(nonscalar_data_range) >=
5645  std::get<0>(nonscalar_data_range),
5646  ExcLowerRange(std::get<1>(nonscalar_data_range),
5647  std::get<0>(nonscalar_data_range)));
5648  AssertThrow(std::get<1>(nonscalar_data_range) < n_data_sets,
5649  ExcIndexRange(std::get<1>(nonscalar_data_range),
5650  0,
5651  n_data_sets));
5652  AssertThrow(std::get<1>(nonscalar_data_range) + 1 -
5653  std::get<0>(nonscalar_data_range) <=
5654  3,
5655  ExcMessage(
5656  "Can't declare a vector with more than 3 components "
5657  "in VTK"));
5658 
5659  // mark these components as already written:
5660  for (unsigned int i = std::get<0>(nonscalar_data_range);
5661  i <= std::get<1>(nonscalar_data_range);
5662  ++i)
5663  data_set_written[i] = true;
5664 
5665  // write the header. concatenate all the component names with double
5666  // underscores unless a vector name has been specified
5667  out << "VECTORS ";
5668 
5669  if (!std::get<2>(nonscalar_data_range).empty())
5670  out << std::get<2>(nonscalar_data_range);
5671  else
5672  {
5673  for (unsigned int i = std::get<0>(nonscalar_data_range);
5674  i < std::get<1>(nonscalar_data_range);
5675  ++i)
5676  out << data_names[i] << "__";
5677  out << data_names[std::get<1>(nonscalar_data_range)];
5678  }
5679 
5680  out << " double" << '\n';
5681 
5682  // now write data. pad all vectors to have three components
5683  for (unsigned int n = 0; n < n_nodes; ++n)
5684  {
5685  switch (std::get<1>(nonscalar_data_range) -
5686  std::get<0>(nonscalar_data_range))
5687  {
5688  case 0:
5689  out << data_vectors(std::get<0>(nonscalar_data_range), n)
5690  << " 0 0" << '\n';
5691  break;
5692 
5693  case 1:
5694  out << data_vectors(std::get<0>(nonscalar_data_range), n)
5695  << ' '
5696  << data_vectors(std::get<0>(nonscalar_data_range) + 1, n)
5697  << " 0" << '\n';
5698  break;
5699  case 2:
5700  out << data_vectors(std::get<0>(nonscalar_data_range), n)
5701  << ' '
5702  << data_vectors(std::get<0>(nonscalar_data_range) + 1, n)
5703  << ' '
5704  << data_vectors(std::get<0>(nonscalar_data_range) + 2, n)
5705  << '\n';
5706  break;
5707 
5708  default:
5709  // VTK doesn't support anything else than vectors with 1, 2,
5710  // or 3 components
5711  Assert(false, ExcInternalError());
5712  }
5713  }
5714  }
5715 
5716  // now do the left over scalar data sets
5717  for (unsigned int data_set = 0; data_set < n_data_sets; ++data_set)
5718  if (data_set_written[data_set] == false)
5719  {
5720  out << "SCALARS " << data_names[data_set] << " double 1" << '\n'
5721  << "LOOKUP_TABLE default" << '\n';
5722  std::copy(data_vectors[data_set].begin(),
5723  data_vectors[data_set].end(),
5724  std::ostream_iterator<double>(out, " "));
5725  out << '\n';
5726  }
5727 
5728  // make sure everything now gets to disk
5729  out.flush();
5730 
5731  // assert the stream is still ok
5732  AssertThrow(out.fail() == false, ExcIO());
5733  }
5734 
5735 
5736  void
5737  write_vtu_header(std::ostream &out, const VtkFlags &flags)
5738  {
5739  AssertThrow(out.fail() == false, ExcIO());
5740  out << "<?xml version=\"1.0\" ?> \n";
5741  out << "<!-- \n";
5742  out << "# vtk DataFile Version 3.0" << '\n'
5743  << "#This file was generated by the deal.II library";
5744  if (flags.print_date_and_time)
5745  {
5746  out << " on " << Utilities::System::get_time() << " at "
5748  }
5749  else
5750  out << '.';
5751  out << "\n-->\n";
5752  out << "<VTKFile type=\"UnstructuredGrid\" version=\"0.1\"";
5753 #ifdef DEAL_II_WITH_ZLIB
5754  out << " compressor=\"vtkZLibDataCompressor\"";
5755 #endif
5756 #ifdef DEAL_II_WORDS_BIGENDIAN
5757  out << " byte_order=\"BigEndian\"";
5758 #else
5759  out << " byte_order=\"LittleEndian\"";
5760 #endif
5761  out << ">";
5762  out << '\n';
5763  out << "<UnstructuredGrid>";
5764  out << '\n';
5765  }
5766 
5767 
5768 
5769  void
5770  write_vtu_footer(std::ostream &out)
5771  {
5772  AssertThrow(out.fail() == false, ExcIO());
5773  out << " </UnstructuredGrid>\n";
5774  out << "</VTKFile>\n";
5775  }
5776 
5777 
5778 
5779  template <int dim, int spacedim>
5780  void
5782  const std::vector<Patch<dim, spacedim>> &patches,
5783  const std::vector<std::string> & data_names,
5784  const std::vector<
5785  std::tuple<unsigned int,
5786  unsigned int,
5787  std::string,
5789  & nonscalar_data_ranges,
5790  const VtkFlags &flags,
5791  std::ostream & out)
5792  {
5793  write_vtu_header(out, flags);
5794  write_vtu_main(patches, data_names, nonscalar_data_ranges, flags, out);
5795  write_vtu_footer(out);
5796 
5797  out << std::flush;
5798  }
5799 
5800 
5801  template <int dim, int spacedim>
5802  void
5804  const std::vector<Patch<dim, spacedim>> &patches,
5805  const std::vector<std::string> & data_names,
5806  const std::vector<
5807  std::tuple<unsigned int,
5808  unsigned int,
5809  std::string,
5811  & nonscalar_data_ranges,
5812  const VtkFlags &flags,
5813  std::ostream & out)
5814  {
5815  AssertThrow(out.fail() == false, ExcIO());
5816 
5817  // If the user provided physical units, make sure that they don't contain
5818  // quote characters as this would make the VTU file invalid XML and
5819  // probably lead to all sorts of difficult error messages. Other than that,
5820  // trust the user that whatever they provide makes sense somehow.
5821  for (const auto &unit : flags.physical_units)
5822  {
5823  (void)unit;
5824  Assert(
5825  unit.second.find('\"') == std::string::npos,
5826  ExcMessage(
5827  "A physical unit you provided, <" + unit.second +
5828  ">, contained a quotation mark character. This is not allowed."));
5829  }
5830 
5831 #ifndef DEAL_II_WITH_MPI
5832  // verify that there are indeed patches to be written out. most of the
5833  // times, people just forget to call build_patches when there are no
5834  // patches, so a warning is in order. that said, the assertion is disabled
5835  // if we support MPI since then it can happen that on the coarsest mesh, a
5836  // processor simply has no cells it actually owns, and in that case it is
5837  // legit if there are no patches
5838  Assert(patches.size() > 0, ExcNoPatches());
5839 #else
5840  if (patches.size() == 0)
5841  {
5842  // we still need to output a valid vtu file, because other CPUs might
5843  // output data. This is the minimal file that is accepted by paraview
5844  // and visit. if we remove the field definitions, visit is complaining.
5845  out << "<Piece NumberOfPoints=\"0\" NumberOfCells=\"0\" >\n"
5846  << "<Cells>\n"
5847  << "<DataArray type=\"UInt8\" Name=\"types\"></DataArray>\n"
5848  << "</Cells>\n"
5849  << " <PointData Scalars=\"scalars\">\n";
5850  std::vector<bool> data_set_written(data_names.size(), false);
5851  for (const auto &nonscalar_data_range : nonscalar_data_ranges)
5852  {
5853  // mark these components as already written:
5854  for (unsigned int i = std::get<0>(nonscalar_data_range);
5855  i <= std::get<1>(nonscalar_data_range);
5856  ++i)
5857  data_set_written[i] = true;
5858 
5859  // write the header. concatenate all the component names with double
5860  // underscores unless a vector name has been specified
5861  out << " <DataArray type=\"Float32\" Name=\"";
5862 
5863  if (!std::get<2>(nonscalar_data_range).empty())
5864  out << std::get<2>(nonscalar_data_range);
5865  else
5866  {
5867  for (unsigned int i = std::get<0>(nonscalar_data_range);
5868  i < std::get<1>(nonscalar_data_range);
5869  ++i)
5870  out << data_names[i] << "__";
5871  out << data_names[std::get<1>(nonscalar_data_range)];
5872  }
5873 
5874  out << "\" NumberOfComponents=\"3\"></DataArray>\n";
5875  }
5876 
5877  for (unsigned int data_set = 0; data_set < data_names.size();
5878  ++data_set)
5879  if (data_set_written[data_set] == false)
5880  {
5881  out << " <DataArray type=\"Float32\" Name=\""
5882  << data_names[data_set] << "\"></DataArray>\n";
5883  }
5884 
5885  out << " </PointData>\n";
5886  out << "</Piece>\n";
5887 
5888  out << std::flush;
5889 
5890  return;
5891  }
5892 #endif
5893 
5894  // first up: metadata
5895  //
5896  // if desired, output time and cycle of the simulation, following the
5897  // instructions at
5898  // http://www.visitusers.org/index.php?title=Time_and_Cycle_in_VTK_files
5899  {
5900  const unsigned int n_metadata =
5901  ((flags.cycle != std::numeric_limits<unsigned int>::min() ? 1 : 0) +
5902  (flags.time != std::numeric_limits<double>::min() ? 1 : 0));
5903  if (n_metadata > 0)
5904  out << "<FieldData>\n";
5905 
5907  {
5908  out
5909  << "<DataArray type=\"Float32\" Name=\"CYCLE\" NumberOfTuples=\"1\" format=\"ascii\">"
5910  << flags.cycle << "</DataArray>\n";
5911  }
5912  if (flags.time != std::numeric_limits<double>::min())
5913  {
5914  out
5915  << "<DataArray type=\"Float32\" Name=\"TIME\" NumberOfTuples=\"1\" format=\"ascii\">"
5916  << flags.time << "</DataArray>\n";
5917  }
5918 
5919  if (n_metadata > 0)
5920  out << "</FieldData>\n";
5921  }
5922 
5923 
5924  VtuStream vtu_out(out, flags);
5925 
5926  const unsigned int n_data_sets = data_names.size();
5927  // check against # of data sets in first patch. checks against all other
5928  // patches are made in write_gmv_reorder_data_vectors
5929  if (patches[0].points_are_available)
5930  {
5931  AssertDimension(n_data_sets + spacedim, patches[0].data.n_rows())
5932  }
5933  else
5934  {
5935  AssertDimension(n_data_sets, patches[0].data.n_rows())
5936  }
5937 
5938 #ifdef DEAL_II_WITH_ZLIB
5939  const char *ascii_or_binary = "binary";
5940 #else
5941  const char * ascii_or_binary = "ascii";
5942 #endif
5943 
5944 
5945  // first count the number of cells and cells for later use
5946  unsigned int n_nodes, n_cells, n_points_and_n_cells;
5947  compute_sizes(patches,
5949  n_nodes,
5950  n_cells,
5951  n_points_and_n_cells);
5952 
5953  // in gmv format the vertex coordinates and the data have an order that is a
5954  // bit unpleasant (first all x coordinates, then all y coordinate, ...;
5955  // first all data of variable 1, then variable 2, etc), so we have to copy
5956  // the data vectors a bit around
5957  //
5958  // note that we copy vectors when looping over the patches since we have to
5959  // write them one variable at a time and don't want to use more than one
5960  // loop
5961  //
5962  // this copying of data vectors can be done while we already output the
5963  // vertices, so do this on a separate task and when wanting to write out the
5964  // data, we wait for that task to finish
5965  Table<2, float> data_vectors(n_data_sets, n_nodes);
5966 
5967  void (*fun_ptr)(const std::vector<Patch<dim, spacedim>> &,
5968  Table<2, float> &) =
5969  &write_gmv_reorder_data_vectors<dim, spacedim, float>;
5970  Threads::Task<> reorder_task =
5971  Threads::new_task(fun_ptr, patches, data_vectors);
5972 
5973  //-----------------------------
5974  // first make up a list of used vertices along with their coordinates
5975  //
5976  // note that according to the standard, we have to print d=1..3 dimensions,
5977  // even if we are in reality in 2d, for example
5978  out << "<Piece NumberOfPoints=\"" << n_nodes << "\" NumberOfCells=\""
5979  << n_cells << "\" >\n";
5980  out << " <Points>\n";
5981  out << " <DataArray type=\"Float32\" NumberOfComponents=\"3\" format=\""
5982  << ascii_or_binary << "\">\n";
5983  write_nodes(patches, vtu_out);
5984  out << " </DataArray>\n";
5985  out << " </Points>\n\n";
5986  //-------------------------------
5987  // now for the cells
5988  out << " <Cells>\n";
5989  out << " <DataArray type=\"Int32\" Name=\"connectivity\" format=\""
5990  << ascii_or_binary << "\">\n";
5991  if (flags.write_higher_order_cells)
5992  write_high_order_cells(patches, vtu_out);
5993  else
5994  write_cells(patches, vtu_out);
5995  out << " </DataArray>\n";
5996 
5997  // XML VTU format uses offsets; this is different than the VTK format, which
5998  // puts the number of nodes per cell in front of the connectivity list.
5999  out << " <DataArray type=\"Int32\" Name=\"offsets\" format=\""
6000  << ascii_or_binary << "\">\n";
6001 
6002  std::vector<int32_t> offsets;
6003  offsets.reserve(n_cells);
6004 
6005  // std::uint8_t might be an alias to unsigned char which is then not printed
6006  // as ascii integers
6007 #ifdef DEAL_II_WITH_ZLIB
6008  std::vector<std::uint8_t> cell_types;
6009 #else
6010  std::vector<unsigned int> cell_types;
6011 #endif
6012  cell_types.reserve(n_cells);
6013 
6014  unsigned int first_vertex_of_patch = 0;
6015 
6016  for (const auto &patch : patches)
6017  {
6018  const auto vtk_cell_id =
6019  extract_vtk_patch_info(patch, flags.write_higher_order_cells);
6020 
6021  for (unsigned int i = 0; i < vtk_cell_id[1]; ++i)
6022  {
6023  cell_types.push_back(vtk_cell_id[0]);
6024  first_vertex_of_patch += vtk_cell_id[2];
6025  offsets.push_back(first_vertex_of_patch);
6026  }
6027  }
6028 
6029  vtu_out << offsets;
6030  out << '\n';
6031  out << " </DataArray>\n";
6032 
6033  // next output the types of the cells. since all cells are the same, this is
6034  // simple
6035  out << " <DataArray type=\"UInt8\" Name=\"types\" format=\""
6036  << ascii_or_binary << "\">\n";
6037 
6038  // this should compress well :-)
6039  vtu_out << cell_types;
6040  out << '\n';
6041  out << " </DataArray>\n";
6042  out << " </Cells>\n";
6043 
6044 
6045  //-------------------------------------
6046  // data output.
6047 
6048  // now write the data vectors to @p{out} first make sure that all data is in
6049  // place
6050  reorder_task.join();
6051 
6052  // then write data. the 'POINT_DATA' means: node data (as opposed to cell
6053  // data, which we do not support explicitly here). all following data sets
6054  // are point data
6055  out << " <PointData Scalars=\"scalars\">\n";
6056 
6057  // when writing, first write out all vector data, then handle the scalar
6058  // data sets that have been left over
6059  std::vector<bool> data_set_written(n_data_sets, false);
6060  for (const auto &range : nonscalar_data_ranges)
6061  {
6062  const auto first_component = std::get<0>(range);
6063  const auto last_component = std::get<1>(range);
6064  const auto &name = std::get<2>(range);
6065  const bool is_tensor =
6066  (std::get<3>(range) ==
6068  const unsigned int n_components = (is_tensor ? 9 : 3);
6069  AssertThrow(last_component >= first_component,
6070  ExcLowerRange(last_component, first_component));
6071  AssertThrow(last_component < n_data_sets,
6072  ExcIndexRange(last_component, 0, n_data_sets));
6073  if (is_tensor)
6074  {
6075  AssertThrow((last_component + 1 - first_component <= 9),
6076  ExcMessage(
6077  "Can't declare a tensor with more than 9 components "
6078  "in VTK/VTU format."));
6079  }
6080  else
6081  {
6082  AssertThrow((last_component + 1 - first_component <= 3),
6083  ExcMessage(
6084  "Can't declare a vector with more than 3 components "
6085  "in VTK/VTU format."));
6086  }
6087 
6088  // mark these components as already written:
6089  for (unsigned int i = first_component; i <= last_component; ++i)
6090  data_set_written[i] = true;
6091 
6092  // write the header. concatenate all the component names with double
6093  // underscores unless a vector name has been specified
6094  out << " <DataArray type=\"Float32\" Name=\"";
6095 
6096  if (!name.empty())
6097  out << name;
6098  else
6099  {
6100  for (unsigned int i = first_component; i < last_component; ++i)
6101  out << data_names[i] << "__";
6102  out << data_names[last_component];
6103  }
6104 
6105  out << "\" NumberOfComponents=\"" << n_components << "\" format=\""
6106  << ascii_or_binary << "\"";
6107  // If present, also list the physical units for this quantity. Look this
6108  // up for either the name of the whole vector/tensor, or if that isn't
6109  // listed, via its first component.
6110  if (!name.empty())
6111  {
6112  if (flags.physical_units.find(name) != flags.physical_units.end())
6113  out << " units=\"" << flags.physical_units.at(name) << "\"";
6114  }
6115  else
6116  {
6117  if (flags.physical_units.find(data_names[first_component]) !=
6118  flags.physical_units.end())
6119  out << " units=\""
6120  << flags.physical_units.at(data_names[first_component])
6121  << "\"";
6122  }
6123  out << ">\n";
6124 
6125  // now write data. pad all vectors to have three components
6126  std::vector<float> data;
6127  data.reserve(n_nodes * n_components);
6128 
6129  for (unsigned int n = 0; n < n_nodes; ++n)
6130  {
6131  if (!is_tensor)
6132  {
6133  switch (last_component - first_component)
6134  {
6135  case 0:
6136  data.push_back(data_vectors(first_component, n));
6137  data.push_back(0);
6138  data.push_back(0);
6139  break;
6140 
6141  case 1:
6142  data.push_back(data_vectors(first_component, n));
6143  data.push_back(data_vectors(first_component + 1, n));
6144  data.push_back(0);
6145  break;
6146 
6147  case 2:
6148  data.push_back(data_vectors(first_component, n));
6149  data.push_back(data_vectors(first_component + 1, n));
6150  data.push_back(data_vectors(first_component + 2, n));
6151  break;
6152 
6153  default:
6154  // Anything else is not yet implemented
6155  Assert(false, ExcInternalError());
6156  }
6157  }
6158  else
6159  {
6160  Tensor<2, 3> vtk_data;
6161  vtk_data = 0.;
6162 
6163  const unsigned int size = last_component - first_component + 1;
6164  if (size == 1)
6165  // 1D, 1 element
6166  {
6167  vtk_data[0][0] = data_vectors(first_component, n);
6168  }
6169  else if (size == 4)
6170  // 2D, 4 elements
6171  {
6172  for (unsigned int c = 0; c < size; ++c)
6173  {
6174  const auto ind =
6176  vtk_data[ind[0]][ind[1]] =
6177  data_vectors(first_component + c, n);
6178  }
6179  }
6180  else if (size == 9)
6181  // 3D 9 elements
6182  {
6183  for (unsigned int c = 0; c < size; ++c)
6184  {
6185  const auto ind =
6187  vtk_data[ind[0]][ind[1]] =
6188  data_vectors(first_component + c, n);
6189  }
6190  }
6191  else
6192  {
6193  Assert(false, ExcInternalError());
6194  }
6195 
6196  // now put the tensor into data
6197  // note we padd with zeros because VTK format always wants to
6198  // see a 3x3 tensor, regardless of dimension
6199  for (unsigned int i = 0; i < 3; ++i)
6200  for (unsigned int j = 0; j < 3; ++j)
6201  data.push_back(vtk_data[i][j]);
6202  }
6203  } // loop over nodes
6204 
6205  vtu_out << data;
6206  out << '\n';
6207  out << " </DataArray>\n";
6208 
6209  } // loop over ranges
6210 
6211  // now do the left over scalar data sets
6212  for (unsigned int data_set = 0; data_set < n_data_sets; ++data_set)
6213  if (data_set_written[data_set] == false)
6214  {
6215  out << " <DataArray type=\"Float32\" Name=\""
6216  << data_names[data_set] << "\" format=\"" << ascii_or_binary
6217  << "\"";
6218  // If present, also list the physical units for this quantity.
6219  if (flags.physical_units.find(data_names[data_set]) !=
6220  flags.physical_units.end())
6221  out << " units=\"" << flags.physical_units.at(data_names[data_set])
6222  << "\"";
6223 
6224  out << ">\n";
6225 
6226  std::vector<float> data(data_vectors[data_set].begin(),
6227  data_vectors[data_set].end());
6228  vtu_out << data;
6229  out << '\n';
6230  out << " </DataArray>\n";
6231  }
6232 
6233  out << " </PointData>\n";
6234 
6235  // Finish up writing a valid XML file
6236  out << " </Piece>\n";
6237 
6238  // make sure everything now gets to disk
6239  out.flush();
6240 
6241  // assert the stream is still ok
6242  AssertThrow(out.fail() == false, ExcIO());
6243  }
6244 
6245 
6246 
6247  void
6249  std::ostream & out,
6250  const std::vector<std::string> &piece_names,
6251  const std::vector<std::string> &data_names,
6252  const std::vector<
6253  std::tuple<unsigned int,
6254  unsigned int,
6255  std::string,
6257  & nonscalar_data_ranges,
6258  const VtkFlags &flags)
6259  {
6260  AssertThrow(out.fail() == false, ExcIO());
6261 
6262  // If the user provided physical units, make sure that they don't contain
6263  // quote characters as this would make the VTU file invalid XML and
6264  // probably lead to all sorts of difficult error messages. Other than that,
6265  // trust the user that whatever they provide makes sense somehow.
6266  for (const auto &unit : flags.physical_units)
6267  {
6268  (void)unit;
6269  Assert(
6270  unit.second.find('\"') == std::string::npos,
6271  ExcMessage(
6272  "A physical unit you provided, <" + unit.second +
6273  ">, contained a quotation mark character. This is not allowed."));
6274  }
6275 
6276  const unsigned int n_data_sets = data_names.size();
6277 
6278  out << "<?xml version=\"1.0\"?>\n";
6279 
6280  out << "<!--\n";
6281  out << "#This file was generated by the deal.II library"
6282  << " on " << Utilities::System::get_date() << " at "
6283  << Utilities::System::get_time() << "\n-->\n";
6284 
6285  out
6286  << "<VTKFile type=\"PUnstructuredGrid\" version=\"0.1\" byte_order=\"LittleEndian\">\n";
6287  out << " <PUnstructuredGrid GhostLevel=\"0\">\n";
6288  out << " <PPointData Scalars=\"scalars\">\n";
6289 
6290  // We need to output in the same order as the write_vtu function does:
6291  std::vector<bool> data_set_written(n_data_sets, false);
6292  for (const auto &nonscalar_data_range : nonscalar_data_ranges)
6293  {
6294  const auto first_component = std::get<0>(nonscalar_data_range);
6295  const auto last_component = std::get<1>(nonscalar_data_range);
6296  const bool is_tensor =
6297  (std::get<3>(nonscalar_data_range) ==
6299  const unsigned int n_components = (is_tensor ? 9 : 3);
6300  AssertThrow(last_component >= first_component,
6301  ExcLowerRange(last_component, first_component));
6302  AssertThrow(last_component < n_data_sets,
6303  ExcIndexRange(last_component, 0, n_data_sets));
6304  if (is_tensor)
6305  {
6306  AssertThrow((last_component + 1 - first_component <= 9),
6307  ExcMessage(
6308  "Can't declare a tensor with more than 9 components "
6309  "in VTK"));
6310  }
6311  else
6312  {
6313  Assert((last_component + 1 - first_component <= 3),
6314  ExcMessage(
6315  "Can't declare a vector with more than 3 components "
6316  "in VTK"));
6317  }
6318 
6319  // mark these components as already written:
6320  for (unsigned int i = std::get<0>(nonscalar_data_range);
6321  i <= std::get<1>(nonscalar_data_range);
6322  ++i)
6323  data_set_written[i] = true;
6324 
6325  // write the header. concatenate all the component names with double
6326  // underscores unless a vector name has been specified
6327  out << " <PDataArray type=\"Float32\" Name=\"";
6328 
6329  const std::string &name = std::get<2>(nonscalar_data_range);
6330  if (!name.empty())
6331  out << name;
6332  else
6333  {
6334  for (unsigned int i = std::get<0>(nonscalar_data_range);
6335  i < std::get<1>(nonscalar_data_range);
6336  ++i)
6337  out << data_names[i] << "__";
6338  out << data_names[std::get<1>(nonscalar_data_range)];
6339  }
6340 
6341  out << "\" NumberOfComponents=\"" << n_components
6342  << "\" format=\"ascii\"";
6343  // If present, also list the physical units for this quantity. Look this
6344  // up for either the name of the whole vector/tensor, or if that isn't
6345  // listed, via its first component.
6346  if (!name.empty())
6347  {
6348  if (flags.physical_units.find(name) != flags.physical_units.end())
6349  out << " units=\"" << flags.physical_units.at(name) << "\"";
6350  }
6351  else
6352  {
6353  if (flags.physical_units.find(
6354  data_names[std::get<1>(nonscalar_data_range)]) !=
6355  flags.physical_units.end())
6356  out << " units=\""
6357  << flags.physical_units.at(
6358  data_names[std::get<1>(nonscalar_data_range)])
6359  << "\"";
6360  }
6361 
6362  out << "/>\n";
6363  }
6364 
6365  // Now for the scalar fields
6366  for (unsigned int data_set = 0; data_set < n_data_sets; ++data_set)
6367  if (data_set_written[data_set] == false)
6368  {
6369  out << " <PDataArray type=\"Float32\" Name=\""
6370  << data_names[data_set] << "\" format=\"ascii\"";
6371 
6372  if (flags.physical_units.find(data_names[data_set]) !=
6373  flags.physical_units.end())
6374  out << " units=\"" << flags.physical_units.at(data_names[data_set])
6375  << "\"";
6376 
6377  out << "/>\n";
6378  }
6379 
6380  out << " </PPointData>\n";
6381 
6382  out << " <PPoints>\n";
6383  out << " <PDataArray type=\"Float32\" NumberOfComponents=\"3\"/>\n";
6384  out << " </PPoints>\n";
6385 
6386  for (const auto &piece_name : piece_names)
6387  out << " <Piece Source=\"" << piece_name << "\"/>\n";
6388 
6389  out << " </PUnstructuredGrid>\n";
6390  out << "</VTKFile>\n";
6391 
6392  out.flush();
6393 
6394  // assert the stream is still ok
6395  AssertThrow(out.fail() == false, ExcIO());
6396  }
6397 
6398 
6399 
6400  void
6402  std::ostream & out,
6403  const std::vector<std::pair<double, std::string>> &times_and_names)
6404  {
6405  AssertThrow(out.fail() == false, ExcIO());
6406 
6407  out << "<?xml version=\"1.0\"?>\n";
6408 
6409  out << "<!--\n";
6410  out << "#This file was generated by the deal.II library"
6411  << " on " << Utilities::System::get_date() << " at "
6412  << Utilities::System::get_time() << "\n-->\n";
6413 
6414  out
6415  << "<VTKFile type=\"Collection\" version=\"0.1\" ByteOrder=\"LittleEndian\">\n";
6416  out << " <Collection>\n";
6417 
6418  std::streamsize ss = out.precision();
6419  out.precision(12);
6420 
6421  for (const auto &time_and_name : times_and_names)
6422  out << " <DataSet timestep=\"" << time_and_name.first
6423  << "\" group=\"\" part=\"0\" file=\"" << time_and_name.second
6424  << "\"/>\n";
6425 
6426  out << " </Collection>\n";
6427  out << "</VTKFile>\n";
6428 
6429  out.flush();
6430  out.precision(ss);
6431 
6432  AssertThrow(out.fail() == false, ExcIO());
6433  }
6434 
6435 
6436 
6437  void
6438  write_visit_record(std::ostream & out,
6439  const std::vector<std::string> &piece_names)
6440  {
6441  out << "!NBLOCKS " << piece_names.size() << '\n';
6442  for (const auto &piece_name : piece_names)
6443  out << piece_name << '\n';
6444 
6445  out << std::flush;
6446  }
6447 
6448 
6449 
6450  void
6451  write_visit_record(std::ostream & out,
6452  const std::vector<std::vector<std::string>> &piece_names)
6453  {
6454  AssertThrow(out.fail() == false, ExcIO());
6455 
6456  if (piece_names.size() == 0)
6457  return;
6458 
6459  const double nblocks = piece_names[0].size();
6460  Assert(nblocks > 0,
6461  ExcMessage("piece_names should be a vector of nonempty vectors."));
6462 
6463  out << "!NBLOCKS " << nblocks << '\n';
6464  for (const auto &domain : piece_names)
6465  {
6466  Assert(domain.size() == nblocks,
6467  ExcMessage(
6468  "piece_names should be a vector of equal sized vectors."));
6469  for (const auto &subdomain : domain)
6470  out << subdomain << '\n';
6471  }
6472 
6473  out << std::flush;
6474  }
6475 
6476 
6477 
6478  void
6480  std::ostream &out,
6481  const std::vector<std::pair<double, std::vector<std::string>>>
6482  &times_and_piece_names)
6483  {
6484  AssertThrow(out.fail() == false, ExcIO());
6485 
6486  if (times_and_piece_names.size() == 0)
6487  return;
6488 
6489  const double nblocks = times_and_piece_names[0].second.size();
6490  Assert(
6491  nblocks > 0,
6492  ExcMessage(
6493  "time_and_piece_names should contain nonempty vectors of filenames for every timestep."));
6494 
6495  for (const auto &domain : times_and_piece_names)
6496  out << "!TIME " << domain.first << '\n';
6497 
6498  out << "!NBLOCKS " << nblocks << '\n';
6499  for (const auto &domain : times_and_piece_names)
6500  {
6501  Assert(domain.second.size() == nblocks,
6502  ExcMessage(
6503  "piece_names should be a vector of equal sized vectors."));
6504  for (const auto &subdomain : domain.second)
6505  out << subdomain << '\n';
6506  }
6507 
6508  out << std::flush;
6509  }
6510 
6511 
6512 
6513  template <int dim, int spacedim>
6514  void
6516  const std::vector<Patch<dim, spacedim>> &,
6517  const std::vector<std::string> &,
6518  const std::vector<
6519  std::tuple<unsigned int,
6520  unsigned int,
6521  std::string,
6523  const SvgFlags &,
6524  std::ostream &)
6525  {
6526  Assert(false, ExcNotImplemented());
6527  }
6528 
6529  template <int spacedim>
6530  void
6532  const std::vector<Patch<2, spacedim>> &patches,
6533  const std::vector<std::string> & /*data_names*/,
6534  const std::vector<
6535  std::tuple<unsigned int,
6536  unsigned int,
6537  std::string,
6539  & /*nonscalar_data_ranges*/,
6540  const SvgFlags &flags,
6541  std::ostream & out)
6542  {
6543  const unsigned int height = flags.height;
6544  unsigned int width = flags.width;
6545 
6546  // margin around the plotted area
6547  unsigned int margin_in_percent = 0;
6548  if (flags.margin)
6549  margin_in_percent = 5;
6550 
6551 
6552  // determine the bounding box in the model space
6553  double x_dimension, y_dimension, z_dimension;
6554 
6555  const auto &first_patch = patches[0];
6556 
6557  unsigned int n_subdivisions = first_patch.n_subdivisions;
6558  unsigned int n = n_subdivisions + 1;
6559  const unsigned int d1 = 1;
6560  const unsigned int d2 = n;
6561 
6562  Point<spacedim> projected_point;
6563  std::array<Point<spacedim>, 4> projected_points;
6564 
6565  Point<2> projection_decomposition;
6566  std::array<Point<2>, 4> projection_decompositions;
6567 
6568  projected_point =
6569  get_equispaced_location(first_patch, {0, 0}, n_subdivisions);
6570 
6571  if (first_patch.data.n_rows() != 0)
6572  {
6573  AssertIndexRange(flags.height_vector, first_patch.data.n_rows());
6574  }
6575 
6576  double x_min = projected_point[0];
6577  double x_max = x_min;
6578  double y_min = projected_point[1];
6579  double y_max = y_min;
6580  double z_min = first_patch.data.n_rows() != 0 ?
6581  first_patch.data(flags.height_vector, 0) :
6582  0;
6583  double z_max = z_min;
6584 
6585  // iterate over the patches
6586  for (const auto &patch : patches)
6587  {
6588  n_subdivisions = patch.n_subdivisions;
6589  n = n_subdivisions + 1;
6590 
6591  for (unsigned int i2 = 0; i2 < n_subdivisions; ++i2)
6592  {
6593  for (unsigned int i1 = 0; i1 < n_subdivisions; ++i1)
6594  {
6595  projected_points[0] =
6596  get_equispaced_location(patch, {i1, i2}, n_subdivisions);
6597  projected_points[1] =
6598  get_equispaced_location(patch, {i1 + 1, i2}, n_subdivisions);
6599  projected_points[2] =
6600  get_equispaced_location(patch, {i1, i2 + 1}, n_subdivisions);
6601  projected_points[3] = get_equispaced_location(patch,
6602  {i1 + 1, i2 + 1},
6603  n_subdivisions);
6604 
6605  x_min = std::min(x_min, projected_points[0][0]);
6606  x_min = std::min(x_min, projected_points[1][0]);
6607  x_min = std::min(x_min, projected_points[2][0]);
6608  x_min = std::min(x_min, projected_points[3][0]);
6609 
6610  x_max = std::max(x_max, projected_points[0][0]);
6611  x_max = std::max(x_max, projected_points[1][0]);
6612  x_max = std::max(x_max, projected_points[2][0]);
6613  x_max = std::max(x_max, projected_points[3][0]);
6614 
6615  y_min = std::min(y_min, projected_points[0][1]);
6616  y_min = std::min(y_min, projected_points[1][1]);
6617  y_min = std::min(y_min, projected_points[2][1]);
6618  y_min = std::min(y_min, projected_points[3][1]);
6619 
6620  y_max = std::max(y_max, projected_points[0][1]);
6621  y_max = std::max(y_max, projected_points[1][1]);
6622  y_max = std::max(y_max, projected_points[2][1]);
6623  y_max = std::max(y_max, projected_points[3][1]);
6624 
6625  Assert((flags.height_vector < patch.data.n_rows()) ||
6626  patch.data.n_rows() == 0,
6628  0,
6629  patch.data.n_rows()));
6630 
6631  z_min = std::min<double>(z_min,
6632  patch.data(flags.height_vector,
6633  i1 * d1 + i2 * d2));
6634  z_min = std::min<double>(z_min,
6635  patch.data(flags.height_vector,
6636  (i1 + 1) * d1 + i2 * d2));
6637  z_min = std::min<double>(z_min,
6638  patch.data(flags.height_vector,
6639  i1 * d1 + (i2 + 1) * d2));
6640  z_min =
6641  std::min<double>(z_min,
6642  patch.data(flags.height_vector,
6643  (i1 + 1) * d1 + (i2 + 1) * d2));
6644 
6645  z_max = std::max<double>(z_max,
6646  patch.data(flags.height_vector,
6647  i1 * d1 + i2 * d2));
6648  z_max = std::max<double>(z_max,
6649  patch.data(flags.height_vector,
6650  (i1 + 1) * d1 + i2 * d2));
6651  z_max = std::max<double>(z_max,
6652  patch.data(flags.height_vector,
6653  i1 * d1 + (i2 + 1) * d2));
6654  z_max =
6655  std::max<double>(z_max,
6656  patch.data(flags.height_vector,
6657  (i1 + 1) * d1 + (i2 + 1) * d2));
6658  }
6659  }
6660  }
6661 
6662  x_dimension = x_max - x_min;
6663  y_dimension = y_max - y_min;
6664  z_dimension = z_max - z_min;
6665 
6666 
6667  // set initial camera position
6668  Point<3> camera_position;
6669  Point<3> camera_direction;
6670  Point<3> camera_horizontal;
6671  float camera_focus = 0;
6672 
6673  // translate camera from the origin to the initial position
6674  camera_position[0] = 0.;
6675  camera_position[1] = 0.;
6676  camera_position[2] = z_min + 2. * z_dimension;
6677 
6678  camera_direction[0] = 0.;
6679  camera_direction[1] = 0.;
6680  camera_direction[2] = -1.;
6681 
6682  camera_horizontal[0] = 1.;
6683  camera_horizontal[1] = 0.;
6684  camera_horizontal[2] = 0.;
6685 
6686  camera_focus = .5 * z_dimension;
6687 
6688  Point<3> camera_position_temp;
6689  Point<3> camera_direction_temp;
6690  Point<3> camera_horizontal_temp;
6691 
6692  const float angle_factor = 3.14159265f / 180.f;
6693 
6694  // (I) rotate the camera to the chosen polar angle
6695  camera_position_temp[1] =
6696  std::cos(angle_factor * flags.polar_angle) * camera_position[1] -
6697  std::sin(angle_factor * flags.polar_angle) * camera_position[2];
6698  camera_position_temp[2] =
6699  std::sin(angle_factor * flags.polar_angle) * camera_position[1] +
6700  std::cos(angle_factor * flags.polar_angle) * camera_position[2];
6701 
6702  camera_direction_temp[1] =
6703  std::cos(angle_factor * flags.polar_angle) * camera_direction[1] -
6704  std::sin(angle_factor * flags.polar_angle) * camera_direction[2];
6705  camera_direction_temp[2] =
6706  std::sin(angle_factor * flags.polar_angle) * camera_direction[1] +
6707  std::cos(angle_factor * flags.polar_angle) * camera_direction[2];
6708 
6709  camera_horizontal_temp[1] =
6710  std::cos(angle_factor * flags.polar_angle) * camera_horizontal[1] -
6711  std::sin(angle_factor * flags.polar_angle) * camera_horizontal[2];
6712  camera_horizontal_temp[2] =
6713  std::sin(angle_factor * flags.polar_angle) * camera_horizontal[1] +
6714  std::cos(angle_factor * flags.polar_angle) * camera_horizontal[2];
6715 
6716  camera_position[1] = camera_position_temp[1];
6717  camera_position[2] = camera_position_temp[2];
6718 
6719  camera_direction[1] = camera_direction_temp[1];
6720  camera_direction[2] = camera_direction_temp[2];
6721 
6722  camera_horizontal[1] = camera_horizontal_temp[1];
6723  camera_horizontal[2] = camera_horizontal_temp[2];
6724 
6725  // (II) rotate the camera to the chosen azimuth angle
6726  camera_position_temp[0] =
6727  std::cos(angle_factor * flags.azimuth_angle) * camera_position[0] -
6728  std::sin(angle_factor * flags.azimuth_angle) * camera_position[1];
6729  camera_position_temp[1] =
6730  std::sin(angle_factor * flags.azimuth_angle) * camera_position[0] +
6731  std::cos(angle_factor * flags.azimuth_angle) * camera_position[1];
6732 
6733  camera_direction_temp[0] =
6734  std::cos(angle_factor * flags.azimuth_angle) * camera_direction[0] -
6735  std::sin(angle_factor * flags.azimuth_angle) * camera_direction[1];
6736  camera_direction_temp[1] =
6737  std::sin(angle_factor * flags.azimuth_angle) * camera_direction[0] +
6738  std::cos(angle_factor * flags.azimuth_angle) * camera_direction[1];
6739 
6740  camera_horizontal_temp[0] =
6741  std::cos(angle_factor * flags.azimuth_angle) * camera_horizontal[0] -
6742  std::sin(angle_factor * flags.azimuth_angle) * camera_horizontal[1];
6743  camera_horizontal_temp[1] =
6744  std::sin(angle_factor * flags.azimuth_angle) * camera_horizontal[0] +
6745  std::cos(angle_factor * flags.azimuth_angle) * camera_horizontal[1];
6746 
6747  camera_position[0] = camera_position_temp[0];
6748  camera_position[1] = camera_position_temp[1];
6749 
6750  camera_direction[0] = camera_direction_temp[0];
6751  camera_direction[1] = camera_direction_temp[1];
6752 
6753  camera_horizontal[0] = camera_horizontal_temp[0];
6754  camera_horizontal[1] = camera_horizontal_temp[1];
6755 
6756  // (III) translate the camera
6757  camera_position[0] = x_min + .5 * x_dimension;
6758  camera_position[1] = y_min + .5 * y_dimension;
6759 
6760  camera_position[0] += (z_min + 2. * z_dimension) *
6761  std::sin(angle_factor * flags.polar_angle) *
6762  std::sin(angle_factor * flags.azimuth_angle);
6763  camera_position[1] -= (z_min + 2. * z_dimension) *
6764  std::sin(angle_factor * flags.polar_angle) *
6765  std::cos(angle_factor * flags.azimuth_angle);
6766 
6767 
6768  // determine the bounding box on the projection plane
6769  double x_min_perspective, y_min_perspective;
6770  double x_max_perspective, y_max_perspective;
6771  double x_dimension_perspective, y_dimension_perspective;
6772 
6773  n_subdivisions = first_patch.n_subdivisions;
6774  n = n_subdivisions + 1;
6775 
6776  Point<3> point;
6777 
6778  projected_point =
6779  get_equispaced_location(first_patch, {0, 0}, n_subdivisions);
6780 
6781  if (first_patch.data.n_rows() != 0)
6782  {
6783  AssertIndexRange(flags.height_vector, first_patch.data.n_rows());
6784  }
6785 
6786  point[0] = projected_point[0];
6787  point[1] = projected_point[1];
6788  point[2] = first_patch.data.n_rows() != 0 ?
6789  first_patch.data(flags.height_vector, 0) :
6790  0;
6791 
6792  projection_decomposition = svg_project_point(point,
6793  camera_position,
6794  camera_direction,
6795  camera_horizontal,
6796  camera_focus);
6797 
6798  x_min_perspective = projection_decomposition[0];
6799  x_max_perspective = projection_decomposition[0];
6800  y_min_perspective = projection_decomposition[1];
6801  y_max_perspective = projection_decomposition[1];
6802 
6803  // iterate over the patches
6804  for (const auto &patch : patches)
6805  {
6806  n_subdivisions = patch.n_subdivisions;
6807  for (unsigned int i2 = 0; i2 < n_subdivisions; ++i2)
6808  {
6809  for (unsigned int i1 = 0; i1 < n_subdivisions; ++i1)
6810  {
6811  const std::array<Point<spacedim>, 4> projected_vertices{
6812  {get_equispaced_location(patch, {i1, i2}, n_subdivisions),
6813  get_equispaced_location(patch, {i1 + 1, i2}, n_subdivisions),
6814  get_equispaced_location(patch, {i1, i2 + 1}, n_subdivisions),
6815  get_equispaced_location(patch,
6816  {i1 + 1, i2 + 1},
6817  n_subdivisions)}};
6818 
6819  Assert((flags.height_vector < patch.data.n_rows()) ||
6820  patch.data.n_rows() == 0,
6822  0,
6823  patch.data.n_rows()));
6824 
6825  const std::array<Point<3>, 4> vertices = {
6827  projected_vertices[0][1],
6828  patch.data.n_rows() != 0 ?
6829  patch.data(0, i1 * d1 + i2 * d2) :
6830  0},
6832  projected_vertices[1][1],
6833  patch.data.n_rows() != 0 ?
6834  patch.data(0, (i1 + 1) * d1 + i2 * d2) :
6835  0},
6837  projected_vertices[2][1],
6838  patch.data.n_rows() != 0 ?
6839  patch.data(0, i1 * d1 + (i2 + 1) * d2) :
6840  0},
6842  projected_vertices[3][1],
6843  patch.data.n_rows() != 0 ?
6844  patch.data(0, (i1 + 1) * d1 + (i2 + 1) * d2) :
6845  0}}};
6846 
6847  projection_decompositions = {
6848  {svg_project_point(vertices[0],
6849  camera_position,
6850  camera_direction,
6851  camera_horizontal,
6852  camera_focus),
6853  svg_project_point(vertices[1],
6854  camera_position,
6855  camera_direction,
6856  camera_horizontal,
6857  camera_focus),
6858  svg_project_point(vertices[2],
6859  camera_position,
6860  camera_direction,
6861  camera_horizontal,
6862  camera_focus),
6863  svg_project_point(vertices[3],
6864  camera_position,
6865  camera_direction,
6866  camera_horizontal,
6867  camera_focus)}};
6868 
6869  x_min_perspective =
6870  std::min(x_min_perspective,
6871  static_cast<double>(
6872  projection_decompositions[0][0]));
6873  x_min_perspective =
6874  std::min(x_min_perspective,
6875  static_cast<double>(
6876  projection_decompositions[1][0]));
6877  x_min_perspective =
6878  std::min(x_min_perspective,
6879  static_cast<double>(
6880  projection_decompositions[2][0]));
6881  x_min_perspective =
6882  std::min(x_min_perspective,
6883  static_cast<double>(
6884  projection_decompositions[3][0]));
6885 
6886  x_max_perspective =
6887  std::max(x_max_perspective,
6888  static_cast<double>(
6889  projection_decompositions[0][0]));
6890  x_max_perspective =
6891  std::max(x_max_perspective,
6892  static_cast<double>(
6893  projection_decompositions[1][0]));
6894  x_max_perspective =
6895  std::max(x_max_perspective,
6896  static_cast<double>(
6897  projection_decompositions[2][0]));
6898  x_max_perspective =
6899  std::max(x_max_perspective,
6900  static_cast<double>(
6901  projection_decompositions[3][0]));
6902 
6903  y_min_perspective =
6904  std::min(y_min_perspective,
6905  static_cast<double>(
6906  projection_decompositions[0][1]));
6907  y_min_perspective =
6908  std::min(y_min_perspective,
6909  static_cast<double>(
6910  projection_decompositions[1][1]));
6911  y_min_perspective =
6912  std::min(y_min_perspective,
6913  static_cast<double>(
6914  projection_decompositions[2][1]));
6915  y_min_perspective =
6916  std::min(y_min_perspective,
6917  static_cast<double>(
6918  projection_decompositions[3][1]));
6919 
6920  y_max_perspective =
6921  std::max(y_max_perspective,
6922  static_cast<double>(
6923  projection_decompositions[0][1]));
6924  y_max_perspective =
6925  std::max(y_max_perspective,
6926  static_cast<double>(
6927  projection_decompositions[1][1]));
6928  y_max_perspective =
6929  std::max(y_max_perspective,
6930  static_cast<double>(
6931  projection_decompositions[2][1]));
6932  y_max_perspective =
6933  std::max(y_max_perspective,
6934  static_cast<double>(
6935  projection_decompositions[3][1]));
6936  }
6937  }
6938  }
6939 
6940  x_dimension_perspective = x_max_perspective - x_min_perspective;
6941  y_dimension_perspective = y_max_perspective - y_min_perspective;
6942 
6943  std::multiset<SvgCell> cells;
6944 
6945  // iterate over the patches
6946  for (const auto &patch : patches)
6947  {
6948  n_subdivisions = patch.n_subdivisions;
6949 
6950  for (unsigned int i2 = 0; i2 < n_subdivisions; ++i2)
6951  {
6952  for (unsigned int i1 = 0; i1 < n_subdivisions; ++i1)
6953  {
6954  const std::array<Point<spacedim>, 4> projected_vertices = {
6955  {get_equispaced_location(patch, {i1, i2}, n_subdivisions),
6956  get_equispaced_location(patch, {i1 + 1, i2}, n_subdivisions),
6957  get_equispaced_location(patch, {i1, i2 + 1}, n_subdivisions),
6958  get_equispaced_location(patch,
6959  {i1 + 1, i2 + 1},
6960  n_subdivisions)}};
6961 
6962  Assert((flags.height_vector < patch.data.n_rows()) ||
6963  patch.data.n_rows() == 0,
6965  0,
6966  patch.data.n_rows()));
6967 
6968  SvgCell cell;
6969 
6970  cell.vertices[0][0] = projected_vertices[0][0];
6971  cell.vertices[0][1] = projected_vertices[0][1];
6972  cell.vertices[0][2] = patch.data.n_rows() != 0 ?
6973  patch.data(0, i1 * d1 + i2 * d2) :
6974  0;
6975 
6976  cell.vertices[1][0] = projected_vertices[1][0];
6977  cell.vertices[1][1] = projected_vertices[1][1];
6978  cell.vertices[1][2] = patch.data.n_rows() != 0 ?
6979  patch.data(0, (i1 + 1) * d1 + i2 * d2) :
6980  0;
6981 
6982  cell.vertices[2][0] = projected_vertices[2][0];
6983  cell.vertices[2][1] = projected_vertices[2][1];
6984  cell.vertices[2][2] = patch.data.n_rows() != 0 ?
6985  patch.data(0, i1 * d1 + (i2 + 1) * d2) :
6986  0;
6987 
6988  cell.vertices[3][0] = projected_vertices[3][0];
6989  cell.vertices[3][1] = projected_vertices[3][1];
6990  cell.vertices[3][2] =
6991  patch.data.n_rows() != 0 ?
6992  patch.data(0, (i1 + 1) * d1 + (i2 + 1) * d2) :
6993  0;
6994 
6995  cell.projected_vertices[0] =
6996  svg_project_point(cell.vertices[0],
6997  camera_position,
6998  camera_direction,
6999  camera_horizontal,
7000  camera_focus);
7001  cell.projected_vertices[1] =
7002  svg_project_point(cell.vertices[1],
7003  camera_position,
7004  camera_direction,
7005  camera_horizontal,
7006  camera_focus);
7007  cell.projected_vertices[2] =
7008  svg_project_point(cell.vertices[2],
7009  camera_position,
7010  camera_direction,
7011  camera_horizontal,
7012  camera_focus);
7013  cell.projected_vertices[3] =
7014  svg_project_point(cell.vertices[3],
7015  camera_position,
7016  camera_direction,
7017  camera_horizontal,
7018  camera_focus);
7019 
7020  cell.center = .25 * (cell.vertices[0] + cell.vertices[1] +
7021  cell.vertices[2] + cell.vertices[3]);
7022  cell.projected_center = svg_project_point(cell.center,
7023  camera_position,
7024  camera_direction,
7025  camera_horizontal,
7026  camera_focus);
7027 
7028  cell.depth = cell.center.distance(camera_position);
7029 
7030  cells.insert(cell);
7031  }
7032  }
7033  }
7034 
7035 
7036  // write the svg file
7037  if (width == 0)
7038  width = static_cast<unsigned int>(
7039  .5 + height * (x_dimension_perspective / y_dimension_perspective));
7040  unsigned int additional_width = 0;
7041 
7042  if (flags.draw_colorbar)
7043  additional_width = static_cast<unsigned int>(
7044  .5 + height * .3); // additional width for colorbar
7045 
7046  // basic svg header and background rectangle
7047  out << "<svg width=\"" << width + additional_width << "\" height=\""
7048  << height << "\" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"
7049  << '\n'
7050  << " <rect width=\"" << width + additional_width << "\" height=\""
7051  << height << "\" style=\"fill:white\"/>" << '\n'
7052  << '\n';
7053 
7054  unsigned int triangle_counter = 0;
7055 
7056  // write the cells in the correct order
7057  for (const auto &cell : cells)
7058  {
7059  Point<3> points3d_triangle[3];
7060 
7061  for (unsigned int triangle_index = 0; triangle_index < 4;
7062  triangle_index++)
7063  {
7064  switch (triangle_index)
7065  {
7066  case 0:
7067  points3d_triangle[0] = cell.vertices[0],
7068  points3d_triangle[1] = cell.vertices[1],
7069  points3d_triangle[2] = cell.center;
7070  break;
7071  case 1:
7072  points3d_triangle[0] = cell.vertices[1],
7073  points3d_triangle[1] = cell.vertices[3],
7074  points3d_triangle[2] = cell.center;
7075  break;
7076  case 2:
7077  points3d_triangle[0] = cell.vertices[3],
7078  points3d_triangle[1] = cell.vertices[2],
7079  points3d_triangle[2] = cell.center;
7080  break;
7081  case 3:
7082  points3d_triangle[0] = cell.vertices[2],
7083  points3d_triangle[1] = cell.vertices[0],
7084  points3d_triangle[2] = cell.center;
7085  break;
7086  default:
7087  break;
7088  }
7089 
7090  Point<6> gradient_param =
7091  svg_get_gradient_parameters(points3d_triangle);
7092 
7093  double start_h =
7094  .667 - ((gradient_param[4] - z_min) / z_dimension) * .667;
7095  double stop_h =
7096  .667 - ((gradient_param[5] - z_min) / z_dimension) * .667;
7097 
7098  unsigned int start_r = 0;
7099  unsigned int start_g = 0;
7100  unsigned int start_b = 0;
7101 
7102  unsigned int stop_r = 0;
7103  unsigned int stop_g = 0;
7104  unsigned int stop_b = 0;
7105 
7106  unsigned int start_i = static_cast<unsigned int>(start_h * 6.);
7107  unsigned int stop_i = static_cast<unsigned int>(stop_h * 6.);
7108 
7109  double start_f = start_h * 6. - start_i;
7110  double start_q = 1. - start_f;
7111 
7112  double stop_f = stop_h * 6. - stop_i;
7113  double stop_q = 1. - stop_f;
7114 
7115  switch (start_i % 6)
7116  {
7117  case 0:
7118  start_r = 255,
7119  start_g = static_cast<unsigned int>(.5 + 255. * start_f);
7120  break;
7121  case 1:
7122  start_r = static_cast<unsigned int>(.5 + 255. * start_q),
7123  start_g = 255;
7124  break;
7125  case 2:
7126  start_g = 255,
7127  start_b = static_cast<unsigned int>(.5 + 255. * start_f);
7128  break;
7129  case 3:
7130  start_g = static_cast<unsigned int>(.5 + 255. * start_q),
7131  start_b = 255;
7132  break;
7133  case 4:
7134  start_r = static_cast<unsigned int>(.5 + 255. * start_f),
7135  start_b = 255;
7136  break;
7137  case 5:
7138  start_r = 255,
7139  start_b = static_cast<unsigned int>(.5 + 255. * start_q);
7140  break;
7141  default:
7142  break;
7143  }
7144 
7145  switch (stop_i % 6)
7146  {
7147  case 0:
7148  stop_r = 255,
7149  stop_g = static_cast<unsigned int>(.5 + 255. * stop_f);
7150  break;
7151  case 1:
7152  stop_r = static_cast<unsigned int>(.5 + 255. * stop_q),
7153  stop_g = 255;
7154  break;
7155  case 2:
7156  stop_g = 255,
7157  stop_b = static_cast<unsigned int>(.5 + 255. * stop_f);
7158  break;
7159  case 3:
7160  stop_g = static_cast<unsigned int>(.5 + 255. * stop_q),
7161  stop_b = 255;
7162  break;
7163  case 4:
7164  stop_r = static_cast<unsigned int>(.5 + 255. * stop_f),
7165  stop_b = 255;
7166  break;
7167  case 5:
7168  stop_r = 255,
7169  stop_b = static_cast<unsigned int>(.5 + 255. * stop_q);
7170  break;
7171  default:
7172  break;
7173  }
7174 
7175  Point<3> gradient_start_point_3d, gradient_stop_point_3d;
7176 
7177  gradient_start_point_3d[0] = gradient_param[0];
7178  gradient_start_point_3d[1] = gradient_param[1];
7179  gradient_start_point_3d[2] = gradient_param[4];
7180 
7181  gradient_stop_point_3d[0] = gradient_param[2];
7182  gradient_stop_point_3d[1] = gradient_param[3];
7183  gradient_stop_point_3d[2] = gradient_param[5];
7184 
7185  Point<2> gradient_start_point =
7186  svg_project_point(gradient_start_point_3d,
7187  camera_position,
7188  camera_direction,
7189  camera_horizontal,
7190  camera_focus);
7191  Point<2> gradient_stop_point =
7192  svg_project_point(gradient_stop_point_3d,
7193  camera_position,
7194  camera_direction,
7195  camera_horizontal,
7196  camera_focus);
7197 
7198  // define linear gradient
7199  out << " <linearGradient id=\"" << triangle_counter
7200  << "\" gradientUnits=\"userSpaceOnUse\" "
7201  << "x1=\""
7202  << static_cast<unsigned int>(
7203  .5 +
7204  ((gradient_start_point[0] - x_min_perspective) /
7205  x_dimension_perspective) *
7206  (width - (width / 100.) * 2. * margin_in_percent) +
7207  ((width / 100.) * margin_in_percent))
7208  << "\" "
7209  << "y1=\""
7210  << static_cast<unsigned int>(
7211  .5 + height - (height / 100.) * margin_in_percent -
7212  ((gradient_start_point[1] - y_min_perspective) /
7213  y_dimension_perspective) *
7214  (height - (height / 100.) * 2. * margin_in_percent))
7215  << "\" "
7216  << "x2=\""
7217  << static_cast<unsigned int>(
7218  .5 +
7219  ((gradient_stop_point[0] - x_min_perspective) /
7220  x_dimension_perspective) *
7221  (width - (width / 100.) * 2. * margin_in_percent) +
7222  ((width / 100.) * margin_in_percent))
7223  << "\" "
7224  << "y2=\""
7225  << static_cast<unsigned int>(
7226  .5 + height - (height / 100.) * margin_in_percent -
7227  ((gradient_stop_point[1] - y_min_perspective) /
7228  y_dimension_perspective) *
7229  (height - (height / 100.) * 2. * margin_in_percent))
7230  << "\""
7231  << ">" << '\n'
7232  << " <stop offset=\"0\" style=\"stop-color:rgb(" << start_r
7233  << "," << start_g << "," << start_b << ")\"/>" << '\n'
7234  << " <stop offset=\"1\" style=\"stop-color:rgb(" << stop_r
7235  << "," << stop_g << "," << stop_b << ")\"/>" << '\n'
7236  << " </linearGradient>" << '\n';
7237 
7238  // draw current triangle
7239  double x1 = 0, y1 = 0, x2 = 0, y2 = 0;
7240  double x3 = cell.projected_center[0];
7241  double y3 = cell.projected_center[1];
7242 
7243  switch (triangle_index)
7244  {
7245  case 0:
7246  x1 = cell.projected_vertices[0][0],
7247  y1 = cell.projected_vertices[0][1],
7248  x2 = cell.projected_vertices[1][0],
7249  y2 = cell.projected_vertices[1][1];
7250  break;
7251  case 1:
7252  x1 = cell.projected_vertices[1][0],
7253  y1 = cell.projected_vertices[1][1],
7254  x2 = cell.projected_vertices[3][0],
7255  y2 = cell.projected_vertices[3][1];
7256  break;
7257  case 2:
7258  x1 = cell.projected_vertices[3][0],
7259  y1 = cell.projected_vertices[3][1],
7260  x2 = cell.projected_vertices[2][0],
7261  y2 = cell.projected_vertices[2][1];
7262  break;
7263  case 3:
7264  x1 = cell.projected_vertices[2][0],
7265  y1 = cell.projected_vertices[2][1],
7266  x2 = cell.projected_vertices[0][0],
7267  y2 = cell.projected_vertices[0][1];
7268  break;
7269  default:
7270  break;
7271  }
7272 
7273  out << " <path d=\"M "
7274  << static_cast<unsigned int>(
7275  .5 +
7276  ((x1 - x_min_perspective) / x_dimension_perspective) *
7277  (width - (width / 100.) * 2. * margin_in_percent) +
7278  ((width / 100.) * margin_in_percent))
7279  << ' '
7280  << static_cast<unsigned int>(
7281  .5 + height - (height / 100.) * margin_in_percent -
7282  ((y1 - y_min_perspective) / y_dimension_perspective) *
7283  (height - (height / 100.) * 2. * margin_in_percent))
7284  << " L "
7285  << static_cast<unsigned int>(
7286  .5 +
7287  ((x2 - x_min_perspective) / x_dimension_perspective) *
7288  (width - (width / 100.) * 2. * margin_in_percent) +
7289  ((width / 100.) * margin_in_percent))
7290  << ' '
7291  << static_cast<unsigned int>(
7292  .5 + height - (height / 100.) * margin_in_percent -
7293  ((y2 - y_min_perspective) / y_dimension_perspective) *
7294  (height - (height / 100.) * 2. * margin_in_percent))
7295  << " L "
7296  << static_cast<unsigned int>(
7297  .5 +
7298  ((x3 - x_min_perspective) / x_dimension_perspective) *
7299  (width - (width / 100.) * 2. * margin_in_percent) +
7300  ((width / 100.) * margin_in_percent))
7301  << ' '
7302  << static_cast<unsigned int>(
7303  .5 + height - (height / 100.) * margin_in_percent -
7304  ((y3 - y_min_perspective) / y_dimension_perspective) *
7305  (height - (height / 100.) * 2. * margin_in_percent))
7306  << " L "
7307  << static_cast<unsigned int>(
7308  .5 +
7309  ((x1 - x_min_perspective) / x_dimension_perspective) *
7310  (width - (width / 100.) * 2. * margin_in_percent) +
7311  ((width / 100.) * margin_in_percent))
7312  << ' '
7313  << static_cast<unsigned int>(
7314  .5 + height - (height / 100.) * margin_in_percent -
7315  ((y1 - y_min_perspective) / y_dimension_perspective) *
7316  (height - (height / 100.) * 2. * margin_in_percent))
7317  << "\" style=\"stroke:black; fill:url(#" << triangle_counter
7318  << "); stroke-width:" << flags.line_thickness << "\"/>" << '\n';
7319 
7320  triangle_counter++;
7321  }
7322  }
7323 
7324 
7325  // draw the colorbar
7326  if (flags.draw_colorbar)
7327  {
7328  out << '\n' << " <!-- colorbar -->" << '\n';
7329 
7330  unsigned int element_height = static_cast<unsigned int>(
7331  ((height / 100.) * (71. - 2. * margin_in_percent)) / 4);
7332  unsigned int element_width =
7333  static_cast<unsigned int>(.5 + (height / 100.) * 2.5);
7334 
7335  additional_width = 0;
7336  if (!flags.margin)
7337  additional_width =
7338  static_cast<unsigned int>(.5 + (height / 100.) * 2.5);
7339 
7340  for (unsigned int index = 0; index < 4; ++index)
7341  {
7342  double start_h = .667 - ((index + 1) / 4.) * .667;
7343  double stop_h = .667 - (index / 4.) * .667;
7344 
7345  unsigned int start_r = 0;
7346  unsigned int start_g = 0;
7347  unsigned int start_b = 0;
7348 
7349  unsigned int stop_r = 0;
7350  unsigned int stop_g = 0;
7351  unsigned int stop_b = 0;
7352 
7353  unsigned int start_i = static_cast<unsigned int>(start_h * 6.);
7354  unsigned int stop_i = static_cast<unsigned int>(stop_h * 6.);
7355 
7356  double start_f = start_h * 6. - start_i;
7357  double start_q = 1. - start_f;
7358 
7359  double stop_f = stop_h * 6. - stop_i;
7360  double stop_q = 1. - stop_f;
7361 
7362  switch (start_i % 6)
7363  {
7364  case 0:
7365  start_r = 255,
7366  start_g = static_cast<unsigned int>(.5 + 255. * start_f);
7367  break;
7368  case 1:
7369  start_r = static_cast<unsigned int>(.5 + 255. * start_q),
7370  start_g = 255;
7371  break;
7372  case 2:
7373  start_g = 255,
7374  start_b = static_cast<unsigned int>(.5 + 255. * start_f);
7375  break;
7376  case 3:
7377  start_g = static_cast<unsigned int>(.5 + 255. * start_q),
7378  start_b = 255;
7379  break;
7380  case 4:
7381  start_r = static_cast<unsigned int>(.5 + 255. * start_f),
7382  start_b = 255;
7383  break;
7384  case 5:
7385  start_r = 255,
7386  start_b = static_cast<unsigned int>(.5 + 255. * start_q);
7387  break;
7388  default:
7389  break;
7390  }
7391 
7392  switch (stop_i % 6)
7393  {
7394  case 0:
7395  stop_r = 255,
7396  stop_g = static_cast<unsigned int>(.5 + 255. * stop_f);
7397  break;
7398  case 1:
7399  stop_r = static_cast<unsigned int>(.5 + 255. * stop_q),
7400  stop_g = 255;
7401  break;
7402  case 2:
7403  stop_g = 255,
7404  stop_b = static_cast<unsigned int>(.5 + 255. * stop_f);
7405  break;
7406  case 3:
7407  stop_g = static_cast<unsigned int>(.5 + 255. * stop_q),
7408  stop_b = 255;
7409  break;
7410  case 4:
7411  stop_r = static_cast<unsigned int>(.5 + 255. * stop_f),
7412  stop_b = 255;
7413  break;
7414  case 5:
7415  stop_r = 255,
7416  stop_b = static_cast<unsigned int>(.5 + 255. * stop_q);
7417  break;
7418  default:
7419  break;
7420  }
7421 
7422  // define gradient
7423  out << " <linearGradient id=\"colorbar_" << index
7424  << "\" gradientUnits=\"userSpaceOnUse\" "
7425  << "x1=\"" << width + additional_width << "\" "
7426  << "y1=\""
7427  << static_cast<unsigned int>(.5 + (height / 100.) *
7428  (margin_in_percent + 29)) +
7429  (3 - index) * element_height
7430  << "\" "
7431  << "x2=\"" << width + additional_width << "\" "
7432  << "y2=\""
7433  << static_cast<unsigned int>(.5 + (height / 100.) *
7434  (margin_in_percent + 29)) +
7435  (4 - index) * element_height
7436  << "\""
7437  << ">" << '\n'
7438  << " <stop offset=\"0\" style=\"stop-color:rgb(" << start_r
7439  << "," << start_g << "," << start_b << ")\"/>" << '\n'
7440  << " <stop offset=\"1\" style=\"stop-color:rgb(" << stop_r
7441  << "," << stop_g << "," << stop_b << ")\"/>" << '\n'
7442  << " </linearGradient>" << '\n';
7443 
7444  // draw box corresponding to the gradient above
7445  out
7446  << " <rect"
7447  << " x=\"" << width + additional_width << "\" y=\""
7448  << static_cast<unsigned int>(.5 + (height / 100.) *
7449  (margin_in_percent + 29)) +
7450  (3 - index) * element_height
7451  << "\" width=\"" << element_width << "\" height=\""
7452  << element_height
7453  << "\" style=\"stroke:black; stroke-width:2; fill:url(#colorbar_"
7454  << index << ")\"/>" << '\n';
7455  }
7456 
7457  for (unsigned int index = 0; index < 5; ++index)
7458  {
7459  out
7460  << " <text x=\""
7461  << width + additional_width +
7462  static_cast<unsigned int>(1.5 * element_width)
7463  << "\" y=\""
7464  << static_cast<unsigned int>(
7465  .5 + (height / 100.) * (margin_in_percent + 29) +
7466  (4. - index) * element_height + 30.)
7467  << "\""
7468  << " style=\"text-anchor:start; font-size:80; font-family:Helvetica";
7469 
7470  if (index == 0 || index == 4)
7471  out << "; font-weight:bold";
7472 
7473  out << "\">"
7474  << static_cast<float>(
7475  (static_cast<int>((z_min + index * (z_dimension / 4.)) *
7476  10000)) /
7477  10000.);
7478 
7479  if (index == 4)
7480  out << " max";
7481  if (index == 0)
7482  out << " min";
7483 
7484  out << "</text>" << '\n';
7485  }
7486  }
7487 
7488  // finalize the svg file
7489  out << '\n' << "</svg>";
7490  out.flush();
7491  }
7492 
7493 
7494 
7495  template <int dim, int spacedim>
7496  void
7498  const std::vector<Patch<dim, spacedim>> &patches,
7499  const std::vector<std::string> & data_names,
7500  const std::vector<
7501  std::tuple<unsigned int,
7502  unsigned int,
7503  std::string,
7505  &nonscalar_data_ranges,
7506  const Deal_II_IntermediateFlags & /*flags*/,
7507  std::ostream &out)
7508  {
7509  AssertThrow(out.fail() == false, ExcIO());
7510 
7511  // first write tokens indicating the template parameters. we need this in
7512  // here because we may want to read in data again even if we don't know in
7513  // advance the template parameters:
7514  out << dim << ' ' << spacedim << '\n';
7515 
7516  // then write a header
7517  out << "[deal.II intermediate format graphics data]" << '\n'
7518  << "[written by " << DEAL_II_PACKAGE_NAME << " "
7519  << DEAL_II_PACKAGE_VERSION << "]" << '\n'
7520  << "[Version: " << Deal_II_IntermediateFlags::format_version << "]"
7521  << '\n';
7522 
7523  out << data_names.size() << '\n';
7524  for (const auto &data_name : data_names)
7525  out << data_name << '\n';
7526 
7527  out << patches.size() << '\n';
7528  for (unsigned int i = 0; i < patches.size(); ++i)
7529  out << patches[i] << '\n';
7530 
7531  out << nonscalar_data_ranges.size() << '\n';
7532  for (const auto &nonscalar_data_range : nonscalar_data_ranges)
7533  out << std::get<0>(nonscalar_data_range) << ' '
7534  << std::get<1>(nonscalar_data_range) << '\n'
7535  << std::get<2>(nonscalar_data_range) << '\n';
7536 
7537  out << '\n';
7538  // make sure everything now gets to disk
7539  out.flush();
7540  }
7541 
7542 
7543 
7544  std::pair<unsigned int, unsigned int>
7546  {
7547  AssertThrow(input.fail() == false, ExcIO());
7548 
7549  unsigned int dim, spacedim;
7550  input >> dim >> spacedim;
7551 
7552  return std::make_pair(dim, spacedim);
7553  }
7554 } // namespace DataOutBase
7555 
7556 
7557 
7558 /* --------------------------- class DataOutInterface ---------------------- */
7559 
7560 
7561 template <int dim, int spacedim>
7563  : default_subdivisions(1)
7564  , default_fmt(DataOutBase::default_format)
7565 {}
7566 
7567 
7568 
7569 template <int dim, int spacedim>
7570 void
7572 {
7573  DataOutBase::write_dx(get_patches(),
7574  get_dataset_names(),
7575  get_nonscalar_data_ranges(),
7576  dx_flags,
7577  out);
7578 }
7579 
7580 
7581 
7582 template <int dim, int spacedim>
7583 void
7585 {
7586  DataOutBase::write_ucd(get_patches(),
7587  get_dataset_names(),
7588  get_nonscalar_data_ranges(),
7589  ucd_flags,
7590  out);
7591 }
7592 
7593 
7594 
7595 template <int dim, int spacedim>
7596 void
7598 {
7599  DataOutBase::write_gnuplot(get_patches(),
7600  get_dataset_names(),
7601  get_nonscalar_data_ranges(),
7602  gnuplot_flags,
7603  out);
7604 }
7605 
7606 
7607 
7608 template <int dim, int spacedim>
7609 void
7611 {
7612  DataOutBase::write_povray(get_patches(),
7613  get_dataset_names(),
7614  get_nonscalar_data_ranges(),
7615  povray_flags,
7616  out);
7617 }
7618 
7619 
7620 
7621 template <int dim, int spacedim>
7622 void
7624 {
7625  DataOutBase::write_eps(get_patches(),
7626  get_dataset_names(),
7627  get_nonscalar_data_ranges(),
7628  eps_flags,
7629  out);
7630 }
7631 
7632 
7633 
7634 template <int dim, int spacedim>
7635 void
7637 {
7638  DataOutBase::write_gmv(get_patches(),
7639  get_dataset_names(),
7640  get_nonscalar_data_ranges(),
7641  gmv_flags,
7642  out);
7643 }
7644 
7645 
7646 
7647 template <int dim, int spacedim>
7648 void
7650 {
7651  DataOutBase::write_tecplot(get_patches(),
7652  get_dataset_names(),
7653  get_nonscalar_data_ranges(),
7654  tecplot_flags,
7655  out);
7656 }
7657 
7658 
7659 
7660 template <int dim, int spacedim>
7661 void
7663 {
7664  DataOutBase::write_vtk(get_patches(),
7665  get_dataset_names(),
7666  get_nonscalar_data_ranges(),
7667  vtk_flags,
7668  out);
7669 }
7670 
7671 template <int dim, int spacedim>
7672 void
7674 {
7675  DataOutBase::write_vtu(get_patches(),
7676  get_dataset_names(),
7677  get_nonscalar_data_ranges(),
7678  vtk_flags,
7679  out);
7680 }
7681 
7682 template <int dim, int spacedim>
7683 void
7685 {
7686  DataOutBase::write_svg(get_patches(),
7687  get_dataset_names(),
7688  get_nonscalar_data_ranges(),
7689  svg_flags,
7690  out);
7691 }
7692 
7693 
7694 template <int dim, int spacedim>
7695 void
7697  const std::string &filename,
7698  const MPI_Comm & comm) const
7699 {
7700 #ifndef DEAL_II_WITH_MPI
7701  // without MPI fall back to the normal way to write a vtu file :
7702  (void)comm;
7703 
7704  std::ofstream f(filename);
7705  AssertThrow(f, ExcFileNotOpen(filename));
7706  write_vtu(f);
7707 #else
7708 
7709  const unsigned int myrank = Utilities::MPI::this_mpi_process(comm);
7710  const unsigned int n_ranks = Utilities::MPI::n_mpi_processes(comm);
7711  MPI_Info info;
7712  int ierr = MPI_Info_create(&info);
7713  AssertThrowMPI(ierr);
7714  MPI_File fh;
7715  ierr = MPI_File_open(
7716  comm, filename.c_str(), MPI_MODE_CREATE | MPI_MODE_WRONLY, info, &fh);
7717  AssertThrow(ierr == MPI_SUCCESS, ExcFileNotOpen(filename));
7718 
7719  ierr = MPI_File_set_size(fh, 0); // delete the file contents
7720  AssertThrowMPI(ierr);
7721  // this barrier is necessary, because otherwise others might already write
7722  // while one core is still setting the size to zero.
7723  ierr = MPI_Barrier(comm);
7724  AssertThrowMPI(ierr);
7725  ierr = MPI_Info_free(&info);
7726  AssertThrowMPI(ierr);
7727 
7728  // Define header size so we can broadcast later.
7729  unsigned int header_size;
7730  std::uint64_t footer_offset;
7731 
7732  // write header
7733  if (myrank == 0)
7734  {
7735  std::stringstream ss;
7736  DataOutBase::write_vtu_header(ss, vtk_flags);
7737  header_size = ss.str().size();
7738  // Write the header on rank 0 at the start of a file, i.e., offset 0.
7740  fh, 0, ss.str().c_str(), header_size, MPI_CHAR, MPI_STATUS_IGNORE);
7741  AssertThrowMPI(ierr);
7742  }
7743 
7744  ierr = MPI_Bcast(&header_size, 1, MPI_UNSIGNED, 0, comm);
7745  AssertThrowMPI(ierr);
7746 
7747  {
7748  const auto &patches = get_patches();
7749  const types::global_dof_index my_n_patches = patches.size();
7750  const types::global_dof_index global_n_patches =
7751  Utilities::MPI::sum(my_n_patches, comm);
7752 
7753  // Do not write pieces with 0 cells as this will crash paraview if this is
7754  // the first piece written. But if nobody has any pieces to write (file is
7755  // empty), let processor 0 write their empty data, otherwise the vtk file is
7756  // invalid.
7757  std::stringstream ss;
7758  if (my_n_patches > 0 || (global_n_patches == 0 && myrank == 0))
7760  get_dataset_names(),
7761  get_nonscalar_data_ranges(),
7762  vtk_flags,
7763  ss);
7764 
7765  // Use prefix sum to find specific offset to write at.
7766  const std::uint64_t size_on_proc = ss.str().size();
7767  std::uint64_t prefix_sum = 0;
7768  ierr =
7769  MPI_Exscan(&size_on_proc, &prefix_sum, 1, MPI_UINT64_T, MPI_SUM, comm);
7770  AssertThrowMPI(ierr);
7771 
7772  // Locate specific offset for each processor.
7773  const MPI_Offset offset = static_cast<MPI_Offset>(header_size) + prefix_sum;
7774 
7776  offset,
7777  ss.str().c_str(),
7778  ss.str().size(),
7779  MPI_CHAR,
7780  MPI_STATUS_IGNORE);
7781  AssertThrowMPI(ierr);
7782 
7783  if (myrank == n_ranks - 1)
7784  {
7785  // Locating Footer with offset on last rank.
7786  footer_offset = size_on_proc + offset;
7787 
7788  std::stringstream ss;
7790  const unsigned int footer_size = ss.str().size();
7791 
7792  // Writing footer:
7794  footer_offset,
7795  ss.str().c_str(),
7796  footer_size,
7797  MPI_CHAR,
7798  MPI_STATUS_IGNORE);
7799  AssertThrowMPI(ierr);
7800  }
7801  }
7802 
7803  // Make sure we sync to disk. As written in the standard,
7804  // MPI_File_close() actually already implies a sync but there seems
7805  // to be a bug on at least one configuration (running with multiple
7806  // nodes using OpenMPI 4.1) that requires it. Without this call, the
7807  // footer is sometimes missing.
7808  ierr = MPI_File_sync(fh);
7809  AssertThrowMPI(ierr);
7810 
7811  ierr = MPI_File_close(&fh);
7812  AssertThrowMPI(ierr);
7813 #endif
7814 }
7815 
7816 
7817 
7818 template <int dim, int spacedim>
7819 void
7821  std::ostream & out,
7822  const std::vector<std::string> &piece_names) const
7823 {
7825  piece_names,
7826  get_dataset_names(),
7827  get_nonscalar_data_ranges(),
7828  vtk_flags);
7829 }
7830 
7831 
7832 
7833 template <int dim, int spacedim>
7834 std::string
7836  const std::string &directory,
7837  const std::string &filename_without_extension,
7838  const unsigned int counter,
7839  const MPI_Comm & mpi_communicator,
7840  const unsigned int n_digits_for_counter,
7841  const unsigned int n_groups) const
7842 {
7843  const unsigned int rank = Utilities::MPI::this_mpi_process(mpi_communicator);
7844  const unsigned int n_ranks =
7845  Utilities::MPI::n_mpi_processes(mpi_communicator);
7846  const unsigned int n_files_written =
7847  (n_groups == 0 || n_groups > n_ranks) ? n_ranks : n_groups;
7848 
7849  Assert(n_files_written >= 1, ExcInternalError());
7850  // the "-1" is needed since we use C++ style counting starting with 0, so
7851  // writing 10 files means the filename runs from 0 to 9
7852  const unsigned int n_digits =
7853  Utilities::needed_digits(std::max(0, int(n_files_written) - 1));
7854 
7855  const unsigned int color = rank % n_files_written;
7856  const std::string filename =
7857  directory + filename_without_extension + "_" +
7858  Utilities::int_to_string(counter, n_digits_for_counter) + "." +
7859  Utilities::int_to_string(color, n_digits) + ".vtu";
7860 
7861  if (n_groups == 0 || n_groups > n_ranks)
7862  {
7863  // every processor writes one file
7864  std::ofstream output(filename.c_str());
7865  AssertThrow(output, ExcFileNotOpen(filename));
7866  this->write_vtu(output);
7867  }
7868  else if (n_groups == 1)
7869  {
7870  // write only a single data file in parallel
7871  this->write_vtu_in_parallel(filename.c_str(), mpi_communicator);
7872  }
7873  else
7874  {
7875 #ifdef DEAL_II_WITH_MPI
7876  // write n_groups data files
7877  MPI_Comm comm_group;
7878  int ierr = MPI_Comm_split(mpi_communicator, color, rank, &comm_group);
7879  AssertThrowMPI(ierr);
7880  this->write_vtu_in_parallel(filename.c_str(), comm_group);
7882 #else
7883  AssertThrow(false, ExcMessage("Logical error. Should not arrive here."));
7884 #endif
7885  }
7886 
7887  // write pvtu record
7888  const std::string pvtu_filename =
7889  filename_without_extension + "_" +
7890  Utilities::int_to_string(counter, n_digits_for_counter) + ".pvtu";
7891 
7892  if (rank == 0)
7893  {
7894  std::vector<std::string> filename_vector;
7895  for (unsigned int i = 0; i < n_files_written; ++i)
7896  {
7897  const std::string filename =
7898  filename_without_extension + "_" +
7899  Utilities::int_to_string(counter, n_digits_for_counter) + "." +
7900  Utilities::int_to_string(i, n_digits) + ".vtu";
7901 
7902  filename_vector.emplace_back(filename);
7903  }
7904 
7905  std::ofstream pvtu_output((directory + pvtu_filename).c_str());
7906  this->write_pvtu_record(pvtu_output, filename_vector);
7907  }
7908 
7909  return pvtu_filename;
7910 }
7911 
7912 
7913 
7914 template <int dim, int spacedim>
7915 void
7917  std::ostream &out) const
7918 {
7920  get_dataset_names(),
7921  get_nonscalar_data_ranges(),
7922  deal_II_intermediate_flags,
7923  out);
7924 }
7925 
7926 
7927 template <int dim, int spacedim>
7928 XDMFEntry
7930  const DataOutBase::DataOutFilter &data_filter,
7931  const std::string & h5_filename,
7932  const double cur_time,
7933  const MPI_Comm & comm) const
7934 {
7935  return create_xdmf_entry(
7936  data_filter, h5_filename, h5_filename, cur_time, comm);
7937 }
7938 
7939 
7940 
7941 template <int dim, int spacedim>
7942 XDMFEntry
7944  const DataOutBase::DataOutFilter &data_filter,
7945  const std::string & h5_mesh_filename,
7946  const std::string & h5_solution_filename,
7947  const double cur_time,
7948  const MPI_Comm & comm) const
7949 {
7950  std::uint64_t local_node_cell_count[2], global_node_cell_count[2];
7951 
7952 #ifndef DEAL_II_WITH_HDF5
7953  // throw an exception, but first make sure the compiler does not warn about
7954  // the now unused function arguments
7955  (void)data_filter;
7956  (void)h5_mesh_filename;
7957  (void)h5_solution_filename;
7958  (void)cur_time;
7959  (void)comm;
7960  AssertThrow(false, ExcMessage("XDMF support requires HDF5 to be turned on."));
7961 #endif
7962  AssertThrow(spacedim == 2 || spacedim == 3,
7963  ExcMessage("XDMF only supports 2 or 3 space dimensions."));
7964 
7965  local_node_cell_count[0] = data_filter.n_nodes();
7966  local_node_cell_count[1] = data_filter.n_cells();
7967 
7968  // And compute the global total
7969 #ifdef DEAL_II_WITH_MPI
7970  const int myrank = Utilities::MPI::this_mpi_process(comm);
7971  int ierr = MPI_Allreduce(local_node_cell_count,
7972  global_node_cell_count,
7973  2,
7974  MPI_UINT64_T,
7975  MPI_SUM,
7976  comm);
7977  AssertThrowMPI(ierr);
7978 #else
7979  (void)comm;
7980  const int myrank = 0;
7981  global_node_cell_count[0] = local_node_cell_count[0];
7982  global_node_cell_count[1] = local_node_cell_count[1];
7983 #endif
7984 
7985  // Output the XDMF file only on the root process
7986  if (myrank == 0)
7987  {
7988  XDMFEntry entry(h5_mesh_filename,
7989  h5_solution_filename,
7990  cur_time,
7991  global_node_cell_count[0],
7992  global_node_cell_count[1],
7993  dim,
7994  spacedim);
7995  unsigned int n_data_sets = data_filter.n_data_sets();
7996 
7997  // The vector names generated here must match those generated in the HDF5
7998  // file
7999  unsigned int i;
8000  for (i = 0; i < n_data_sets; ++i)
8001  {
8002  entry.add_attribute(data_filter.get_data_set_name(i),
8003  data_filter.get_data_set_dim(i));
8004  }
8005 
8006  return entry;
8007  }
8008  else
8009  {
8010  return {};
8011  }
8012 }
8013 
8014 template <int dim, int spacedim>
8015 void
8017  const std::vector<XDMFEntry> &entries,
8018  const std::string & filename,
8019  const MPI_Comm & comm) const
8020 {
8021 #ifdef DEAL_II_WITH_MPI
8022  const int myrank = Utilities::MPI::this_mpi_process(comm);
8023 #else
8024  (void)comm;
8025  const int myrank = 0;
8026 #endif
8027 
8028  // Only rank 0 process writes the XDMF file
8029  if (myrank == 0)
8030  {
8031  std::ofstream xdmf_file(filename);
8032 
8033  xdmf_file << "<?xml version=\"1.0\" ?>\n";
8034  xdmf_file << "<!DOCTYPE Xdmf SYSTEM \"Xdmf.dtd\" []>\n";
8035  xdmf_file << "<Xdmf Version=\"2.0\">\n";
8036  xdmf_file << " <Domain>\n";
8037  xdmf_file
8038  << " <Grid Name=\"CellTime\" GridType=\"Collection\" CollectionType=\"Temporal\">\n";
8039 
8040  // Write out all the entries indented
8041  const auto &patches = get_patches();
8042  Assert(patches.size() > 0, DataOutBase::ExcNoPatches());
8043 
8044  // We currently don't support writing mixed meshes:
8045 #ifdef DEBUG
8046  for (const auto &patch : patches)
8047  Assert(patch.reference_cell == patches[0].reference_cell,
8048  ExcNotImplemented());
8049 #endif
8050 
8051  for (const auto &entry : entries)
8052  {
8053  xdmf_file << entry.get_xdmf_content(3, patches[0].reference_cell);
8054  }
8055 
8056  xdmf_file << " </Grid>\n";
8057  xdmf_file << " </Domain>\n";
8058  xdmf_file << "</Xdmf>\n";
8059 
8060  xdmf_file.close();
8061  }
8062 }
8063 
8064 
8065 
8066 /*
8067  * Write the data in this DataOutInterface to a DataOutFilter object. Filtering
8068  * is performed based on the DataOutFilter flags.
8069  */
8070 template <int dim, int spacedim>
8071 void
8073  DataOutBase::DataOutFilter &filtered_data) const
8074 {
8075  DataOutBase::write_filtered_data(get_patches(),
8076  get_dataset_names(),
8077  get_nonscalar_data_ranges(),
8078  filtered_data);
8079 }
8080 
8081 
8082 
8083 template <int dim, int spacedim>
8084 void
8086  const std::vector<Patch<dim, spacedim>> &patches,
8087  const std::vector<std::string> & data_names,
8088  const std::vector<
8089  std::tuple<unsigned int,
8090  unsigned int,
8091  std::string,
8093  & nonscalar_data_ranges,
8094  DataOutBase::DataOutFilter &filtered_data)
8095 {
8096  const unsigned int n_data_sets = data_names.size();
8097  unsigned int n_node, n_cell;
8098  Table<2, double> data_vectors;
8099  Threads::Task<> reorder_task;
8100 
8101 #ifndef DEAL_II_WITH_MPI
8102  // verify that there are indeed patches to be written out. most of the times,
8103  // people just forget to call build_patches when there are no patches, so a
8104  // warning is in order. that said, the assertion is disabled if we support MPI
8105  // since then it can happen that on the coarsest mesh, a processor simply has
8106  // no cells it actually owns, and in that case it is legit if there are no
8107  // patches
8108  Assert(patches.size() > 0, ExcNoPatches());
8109 #else
8110  if (patches.size() == 0)
8111  return;
8112 #endif
8113 
8114  compute_sizes<dim, spacedim>(patches, n_node, n_cell);
8115 
8116  data_vectors = Table<2, double>(n_data_sets, n_node);
8117  void (*fun_ptr)(const std::vector<Patch<dim, spacedim>> &,
8118  Table<2, double> &) =
8119  &DataOutBase::template write_gmv_reorder_data_vectors<dim, spacedim>;
8120  reorder_task = Threads::new_task(fun_ptr, patches, data_vectors);
8121 
8122  // Write the nodes/cells to the DataOutFilter object.
8123  write_nodes(patches, filtered_data);
8124  write_cells(patches, filtered_data);
8125 
8126  // Ensure reordering is done before we output data set values
8127  reorder_task.join();
8128 
8129  // when writing, first write out all vector data, then handle the scalar data
8130  // sets that have been left over
8131  unsigned int i, n_th_vector, data_set, pt_data_vector_dim;
8132  std::string vector_name;
8133  for (n_th_vector = 0, data_set = 0; data_set < n_data_sets;)
8134  {
8135  // Advance n_th_vector to at least the current data set we are on
8136  while (n_th_vector < nonscalar_data_ranges.size() &&
8137  std::get<0>(nonscalar_data_ranges[n_th_vector]) < data_set)
8138  n_th_vector++;
8139 
8140  // Determine the dimension of this data
8141  if (n_th_vector < nonscalar_data_ranges.size() &&
8142  std::get<0>(nonscalar_data_ranges[n_th_vector]) == data_set)
8143  {
8144  // Multiple dimensions
8145  pt_data_vector_dim = std::get<1>(nonscalar_data_ranges[n_th_vector]) -
8146  std::get<0>(nonscalar_data_ranges[n_th_vector]) +
8147  1;
8148 
8149  // Ensure the dimensionality of the data is correct
8150  AssertThrow(
8151  std::get<1>(nonscalar_data_ranges[n_th_vector]) >=
8152  std::get<0>(nonscalar_data_ranges[n_th_vector]),
8153  ExcLowerRange(std::get<1>(nonscalar_data_ranges[n_th_vector]),
8154  std::get<0>(nonscalar_data_ranges[n_th_vector])));
8155  AssertThrow(
8156  std::get<1>(nonscalar_data_ranges[n_th_vector]) < n_data_sets,
8157  ExcIndexRange(std::get<1>(nonscalar_data_ranges[n_th_vector]),
8158  0,
8159  n_data_sets));
8160 
8161  // Determine the vector name. Concatenate all the component names with
8162  // double underscores unless a vector name has been specified
8163  if (!std::get<2>(nonscalar_data_ranges[n_th_vector]).empty())
8164  {
8165  vector_name = std::get<2>(nonscalar_data_ranges[n_th_vector]);
8166  }
8167  else
8168  {
8169  vector_name = "";
8170  for (i = std::get<0>(nonscalar_data_ranges[n_th_vector]);
8171  i < std::get<1>(nonscalar_data_ranges[n_th_vector]);
8172  ++i)
8173  vector_name += data_names[i] + "__";
8174  vector_name +=
8175  data_names[std::get<1>(nonscalar_data_ranges[n_th_vector])];
8176  }
8177  }
8178  else
8179  {
8180  // One dimension
8181  pt_data_vector_dim = 1;
8182  vector_name = data_names[data_set];
8183  }
8184 
8185  // Write data to the filter object
8186  filtered_data.write_data_set(vector_name,
8187  pt_data_vector_dim,
8188  data_set,
8189  data_vectors);
8190 
8191  // Advance the current data set
8192  data_set += pt_data_vector_dim;
8193  }
8194 }
8195 
8196 
8197 
8198 template <int dim, int spacedim>
8199 void
8201  const DataOutBase::DataOutFilter &data_filter,
8202  const std::string & filename,
8203  const MPI_Comm & comm) const
8204 {
8205  DataOutBase::write_hdf5_parallel(get_patches(), data_filter, filename, comm);
8206 }
8207 
8208 
8209 
8210 template <int dim, int spacedim>
8211 void
8213  const DataOutBase::DataOutFilter &data_filter,
8214  const bool write_mesh_file,
8215  const std::string & mesh_filename,
8216  const std::string & solution_filename,
8217  const MPI_Comm & comm) const
8218 {
8219  DataOutBase::write_hdf5_parallel(get_patches(),
8220  data_filter,
8221  write_mesh_file,
8222  mesh_filename,
8223  solution_filename,
8224  comm);
8225 }
8226 
8227 
8228 
8229 template <int dim, int spacedim>
8230 void
8232  const std::vector<Patch<dim, spacedim>> &patches,
8233  const DataOutBase::DataOutFilter & data_filter,
8234  const std::string & filename,
8235  const MPI_Comm & comm)
8236 {
8237  write_hdf5_parallel(patches, data_filter, true, filename, filename, comm);
8238 }
8239 
8240 
8241 
8242 template <int dim, int spacedim>
8243 void
8245  const std::vector<Patch<dim, spacedim>> &patches,
8246  const DataOutBase::DataOutFilter & data_filter,
8247  const bool write_mesh_file,
8248  const std::string & mesh_filename,
8249  const std::string & solution_filename,
8250  const MPI_Comm & comm)
8251 {
8252  AssertThrow(
8253  spacedim >= 2,
8254  ExcMessage(
8255  "DataOutBase was asked to write HDF5 output for a space dimension of 1. "
8256  "HDF5 only supports datasets that live in 2 or 3 dimensions."));
8257 
8258  int ierr = 0;
8259  (void)ierr;
8260 #ifndef DEAL_II_WITH_HDF5
8261  // throw an exception, but first make sure the compiler does not warn about
8262  // the now unused function arguments
8263  (void)patches;
8264  (void)data_filter;
8265  (void)write_mesh_file;
8266  (void)mesh_filename;
8267  (void)solution_filename;
8268  (void)comm;
8269  AssertThrow(false, ExcMessage("HDF5 support is disabled."));
8270 #else
8271 # ifndef DEAL_II_WITH_MPI
8272  (void)comm;
8273 # endif
8274 
8275  // verify that there are indeed patches to be written out. most of the times,
8276  // people just forget to call build_patches when there are no patches, so a
8277  // warning is in order. that said, the assertion is disabled if we support MPI
8278  // since then it can happen that on the coarsest mesh, a processor simply has
8279  // no cells it actually owns, and in that case it is legit if there are no
8280  // patches
8281  Assert(patches.size() > 0, ExcNoPatches());
8282 
8283  hid_t h5_mesh_file_id = -1, h5_solution_file_id, file_plist_id, plist_id;
8284  hid_t node_dataspace, node_dataset, node_file_dataspace,
8285  node_memory_dataspace;
8286  hid_t cell_dataspace, cell_dataset, cell_file_dataspace,
8287  cell_memory_dataspace;
8288  hid_t pt_data_dataspace, pt_data_dataset, pt_data_file_dataspace,
8289  pt_data_memory_dataspace;
8290  herr_t status;
8291  std::uint64_t local_node_cell_count[2];
8292  hsize_t count[2], offset[2], node_ds_dim[2], cell_ds_dim[2];
8293  std::vector<double> node_data_vec;
8294  std::vector<unsigned int> cell_data_vec;
8295 
8296  // If HDF5 is not parallel and we're using multiple processes, abort
8297 # ifndef H5_HAVE_PARALLEL
8298 # ifdef DEAL_II_WITH_MPI
8299  int world_size = Utilities::MPI::n_mpi_processes(comm);
8300  AssertThrow(
8301  world_size <= 1,
8302  ExcMessage(
8303  "Serial HDF5 output on multiple processes is not yet supported."));
8304 # endif
8305 # endif
8306 
8307  local_node_cell_count[0] = data_filter.n_nodes();
8308  local_node_cell_count[1] = data_filter.n_cells();
8309 
8310  // Create file access properties
8311  file_plist_id = H5Pcreate(H5P_FILE_ACCESS);
8312  AssertThrow(file_plist_id != -1, ExcIO());
8313  // If MPI is enabled *and* HDF5 is parallel, we can do parallel output
8314 # ifdef DEAL_II_WITH_MPI
8315 # ifdef H5_HAVE_PARALLEL
8316  // Set the access to use the specified MPI_Comm object
8317  status = H5Pset_fapl_mpio(file_plist_id, comm, MPI_INFO_NULL);
8318  AssertThrow(status >= 0, ExcIO());
8319 # endif
8320 # endif
8321 
8322  // Compute the global total number of nodes/cells and determine the offset of
8323  // the data for this process
8324 
8325  std::uint64_t global_node_cell_count[2] = {0, 0};
8326  std::uint64_t global_node_cell_offsets[2] = {0, 0};
8327 
8328 # ifdef DEAL_II_WITH_MPI
8329  ierr = MPI_Allreduce(local_node_cell_count,
8330  global_node_cell_count,
8331  2,
8332  MPI_UINT64_T,
8333  MPI_SUM,
8334  comm);
8335  AssertThrowMPI(ierr);
8336  ierr = MPI_Exscan(local_node_cell_count,
8337  global_node_cell_offsets,
8338  2,
8339  MPI_UINT64_T,
8340  MPI_SUM,
8341  comm);
8342  AssertThrowMPI(ierr);
8343 # else
8344  global_node_cell_count[0] = local_node_cell_count[0];
8345  global_node_cell_count[1] = local_node_cell_count[1];
8346  global_node_cell_offsets[0] = global_node_cell_offsets[1] = 0;
8347 # endif
8348 
8349  // Create the property list for a collective write
8350  plist_id = H5Pcreate(H5P_DATASET_XFER);
8351  AssertThrow(plist_id >= 0, ExcIO());
8352 # ifdef DEAL_II_WITH_MPI
8353 # ifdef H5_HAVE_PARALLEL
8354  status = H5Pset_dxpl_mpio(plist_id, H5FD_MPIO_COLLECTIVE);
8355  AssertThrow(status >= 0, ExcIO());
8356 # endif
8357 # endif
8358 
8359  if (write_mesh_file)
8360  {
8361  // Overwrite any existing files (change this to an option?)
8362  h5_mesh_file_id = H5Fcreate(mesh_filename.c_str(),
8363  H5F_ACC_TRUNC,
8364  H5P_DEFAULT,
8365  file_plist_id);
8366  AssertThrow(h5_mesh_file_id >= 0, ExcIO());
8367 
8368  // Create the dataspace for the nodes and cells. HDF5 only supports 2- or
8369  // 3-dimensional coordinates
8370  node_ds_dim[0] = global_node_cell_count[0];
8371  node_ds_dim[1] = (spacedim < 2) ? 2 : spacedim;
8372  node_dataspace = H5Screate_simple(2, node_ds_dim, nullptr);
8373  AssertThrow(node_dataspace >= 0, ExcIO());
8374 
8375  cell_ds_dim[0] = global_node_cell_count[1];
8376  cell_ds_dim[1] = patches[0].reference_cell.n_vertices();
8377  cell_dataspace = H5Screate_simple(2, cell_ds_dim, nullptr);
8378  AssertThrow(cell_dataspace >= 0, ExcIO());
8379 
8380  // Create the dataset for the nodes and cells
8381 # if H5Gcreate_vers == 1
8382  node_dataset = H5Dcreate(h5_mesh_file_id,
8383  "nodes",
8384  H5T_NATIVE_DOUBLE,
8385  node_dataspace,
8386  H5P_DEFAULT);
8387 # else
8388  node_dataset = H5Dcreate(h5_mesh_file_id,
8389  "nodes",
8390  H5T_NATIVE_DOUBLE,
8391  node_dataspace,
8392  H5P_DEFAULT,
8393  H5P_DEFAULT,
8394  H5P_DEFAULT);
8395 # endif
8396  AssertThrow(node_dataset >= 0, ExcIO());
8397 # if H5Gcreate_vers == 1
8398  cell_dataset = H5Dcreate(
8399  h5_mesh_file_id, "cells", H5T_NATIVE_UINT, cell_dataspace, H5P_DEFAULT);
8400 # else
8401  cell_dataset = H5Dcreate(h5_mesh_file_id,
8402  "cells",
8403  H5T_NATIVE_UINT,
8404  cell_dataspace,
8405  H5P_DEFAULT,
8406  H5P_DEFAULT,
8407  H5P_DEFAULT);
8408 # endif
8409  AssertThrow(cell_dataset >= 0, ExcIO());
8410 
8411  // Close the node and cell dataspaces since we're done with them
8412  status = H5Sclose(node_dataspace);
8413  AssertThrow(status >= 0, ExcIO());
8414  status = H5Sclose(cell_dataspace);
8415  AssertThrow(status >= 0, ExcIO());
8416 
8417  // Create the data subset we'll use to read from memory. HDF5 only
8418  // supports 2- or 3-dimensional coordinates
8419  count[0] = local_node_cell_count[0];
8420  count[1] = (spacedim < 2) ? 2 : spacedim;
8421 
8422  offset[0] = global_node_cell_offsets[0];
8423  offset[1] = 0;
8424 
8425  node_memory_dataspace = H5Screate_simple(2, count, nullptr);
8426  AssertThrow(node_memory_dataspace >= 0, ExcIO());
8427 
8428  // Select the hyperslab in the file
8429  node_file_dataspace = H5Dget_space(node_dataset);
8430  AssertThrow(node_file_dataspace >= 0, ExcIO());
8431  status = H5Sselect_hyperslab(
8432  node_file_dataspace, H5S_SELECT_SET, offset, nullptr, count, nullptr);
8433  AssertThrow(status >= 0, ExcIO());
8434 
8435  // And repeat for cells
8436  count[0] = local_node_cell_count[1];
8437  count[1] = patches[0].reference_cell.n_vertices();
8438  offset[0] = global_node_cell_offsets[1];
8439  offset[1] = 0;
8440  cell_memory_dataspace = H5Screate_simple(2, count, nullptr);
8441  AssertThrow(cell_memory_dataspace >= 0, ExcIO());
8442 
8443  cell_file_dataspace = H5Dget_space(cell_dataset);
8444  AssertThrow(cell_file_dataspace >= 0, ExcIO());
8445  status = H5Sselect_hyperslab(
8446  cell_file_dataspace, H5S_SELECT_SET, offset, nullptr, count, nullptr);
8447  AssertThrow(status >= 0, ExcIO());
8448 
8449  // And finally, write the node data
8450  data_filter.fill_node_data(node_data_vec);
8451  status = H5Dwrite(node_dataset,
8452  H5T_NATIVE_DOUBLE,
8453  node_memory_dataspace,
8454  node_file_dataspace,
8455  plist_id,
8456  node_data_vec.data());
8457  AssertThrow(status >= 0, ExcIO());
8458  node_data_vec.clear();
8459 
8460  // And the cell data
8461  data_filter.fill_cell_data(global_node_cell_offsets[0], cell_data_vec);
8462  status = H5Dwrite(cell_dataset,
8463  H5T_NATIVE_UINT,
8464  cell_memory_dataspace,
8465  cell_file_dataspace,
8466  plist_id,
8467  cell_data_vec.data());
8468  AssertThrow(status >= 0, ExcIO());
8469  cell_data_vec.clear();
8470 
8471  // Close the file dataspaces
8472  status = H5Sclose(node_file_dataspace);
8473  AssertThrow(status >= 0, ExcIO());
8474  status = H5Sclose(cell_file_dataspace);
8475  AssertThrow(status >= 0, ExcIO());
8476 
8477  // Close the memory dataspaces
8478  status = H5Sclose(node_memory_dataspace);
8479  AssertThrow(status >= 0, ExcIO());
8480  status = H5Sclose(cell_memory_dataspace);
8481  AssertThrow(status >= 0, ExcIO());
8482 
8483  // Close the datasets
8484  status = H5Dclose(node_dataset);
8485  AssertThrow(status >= 0, ExcIO());
8486  status = H5Dclose(cell_dataset);
8487  AssertThrow(status >= 0, ExcIO());
8488 
8489  // If the filenames are different, we need to close the mesh file
8490  if (mesh_filename != solution_filename)
8491  {
8492  status = H5Fclose(h5_mesh_file_id);
8493  AssertThrow(status >= 0, ExcIO());
8494  }
8495  }
8496 
8497  // If the filenames are identical, continue with the same file
8498  if (mesh_filename == solution_filename && write_mesh_file)
8499  {
8500  h5_solution_file_id = h5_mesh_file_id;
8501  }
8502  else
8503  {
8504  // Otherwise we need to open a new file
8505  h5_solution_file_id = H5Fcreate(solution_filename.c_str(),
8506  H5F_ACC_TRUNC,
8507  H5P_DEFAULT,
8508  file_plist_id);
8509  AssertThrow(h5_solution_file_id >= 0, ExcIO());
8510  }
8511 
8512  // when writing, first write out all vector data, then handle the scalar data
8513  // sets that have been left over
8514  unsigned int i;
8515  std::string vector_name;
8516  for (i = 0; i < data_filter.n_data_sets(); ++i)
8517  {
8518  // Allocate space for the point data
8519  // Must be either 1D or 3D
8520  const unsigned int pt_data_vector_dim = data_filter.get_data_set_dim(i);
8521  vector_name = data_filter.get_data_set_name(i);
8522 
8523  // Create the dataspace for the point data
8524  node_ds_dim[0] = global_node_cell_count[0];
8525  node_ds_dim[1] = pt_data_vector_dim;
8526  pt_data_dataspace = H5Screate_simple(2, node_ds_dim, nullptr);
8527  AssertThrow(pt_data_dataspace >= 0, ExcIO());
8528 
8529 # if H5Gcreate_vers == 1
8530  pt_data_dataset = H5Dcreate(h5_solution_file_id,
8531  vector_name.c_str(),
8532  H5T_NATIVE_DOUBLE,
8533  pt_data_dataspace,
8534  H5P_DEFAULT);
8535 # else
8536  pt_data_dataset = H5Dcreate(h5_solution_file_id,
8537  vector_name.c_str(),
8538  H5T_NATIVE_DOUBLE,
8539  pt_data_dataspace,
8540  H5P_DEFAULT,
8541  H5P_DEFAULT,
8542  H5P_DEFAULT);
8543 # endif
8544  AssertThrow(pt_data_dataset >= 0, ExcIO());
8545 
8546  // Create the data subset we'll use to read from memory
8547  count[0] = local_node_cell_count[0];
8548  count[1] = pt_data_vector_dim;
8549  offset[0] = global_node_cell_offsets[0];
8550  offset[1] = 0;
8551  pt_data_memory_dataspace = H5Screate_simple(2, count, nullptr);
8552  AssertThrow(pt_data_memory_dataspace >= 0, ExcIO());
8553 
8554  // Select the hyperslab in the file
8555  pt_data_file_dataspace = H5Dget_space(pt_data_dataset);
8556  AssertThrow(pt_data_file_dataspace >= 0, ExcIO());
8557  status = H5Sselect_hyperslab(pt_data_file_dataspace,
8558  H5S_SELECT_SET,
8559  offset,
8560  nullptr,
8561  count,
8562  nullptr);
8563  AssertThrow(status >= 0, ExcIO());
8564 
8565  // And finally, write the data
8566  status = H5Dwrite(pt_data_dataset,
8567  H5T_NATIVE_DOUBLE,
8568  pt_data_memory_dataspace,
8569  pt_data_file_dataspace,
8570  plist_id,
8571  data_filter.get_data_set(i));
8572  AssertThrow(status >= 0, ExcIO());
8573 
8574  // Close the dataspaces
8575  status = H5Sclose(pt_data_dataspace);
8576  AssertThrow(status >= 0, ExcIO());
8577  status = H5Sclose(pt_data_memory_dataspace);
8578  AssertThrow(status >= 0, ExcIO());
8579  status = H5Sclose(pt_data_file_dataspace);
8580  AssertThrow(status >= 0, ExcIO());
8581  // Close the dataset
8582  status = H5Dclose(pt_data_dataset);
8583  AssertThrow(status >= 0, ExcIO());
8584  }
8585 
8586  // Close the file property list
8587  status = H5Pclose(file_plist_id);
8588  AssertThrow(status >= 0, ExcIO());
8589 
8590  // Close the parallel access
8591  status = H5Pclose(plist_id);
8592  AssertThrow(status >= 0, ExcIO());
8593 
8594  // Close the file
8595  status = H5Fclose(h5_solution_file_id);
8596  AssertThrow(status >= 0, ExcIO());
8597 #endif
8598 }
8599 
8600 
8601 
8602 template <int dim, int spacedim>
8603 void
8605  std::ostream & out,
8606  const DataOutBase::OutputFormat output_format_) const
8607 {
8608  DataOutBase::OutputFormat output_format = output_format_;
8609  if (output_format == DataOutBase::default_format)
8610  output_format = default_fmt;
8611 
8612  switch (output_format)
8613  {
8614  case DataOutBase::none:
8615  break;
8616 
8617  case DataOutBase::dx:
8618  write_dx(out);
8619  break;
8620 
8621  case DataOutBase::ucd:
8622  write_ucd(out);
8623  break;
8624 
8625  case DataOutBase::gnuplot:
8626  write_gnuplot(out);
8627  break;
8628 
8629  case DataOutBase::povray:
8630  write_povray(out);
8631  break;
8632 
8633  case DataOutBase::eps:
8634  write_eps(out);
8635  break;
8636 
8637  case DataOutBase::gmv:
8638  write_gmv(out);
8639  break;
8640 
8641  case DataOutBase::tecplot:
8642  write_tecplot(out);
8643  break;
8644 
8645  case DataOutBase::vtk:
8646  write_vtk(out);
8647  break;
8648 
8649  case DataOutBase::vtu:
8650  write_vtu(out);
8651  break;
8652 
8653  case DataOutBase::svg:
8654  write_svg(out);
8655  break;
8656 
8659  break;
8660 
8661  default:
8662  Assert(false, ExcNotImplemented());
8663  }
8664 }
8665 
8666 
8667 
8668 template <int dim, int spacedim>
8669 void
8671  const DataOutBase::OutputFormat fmt)
8672 {
8674  default_fmt = fmt;
8675 }
8676 
8677 template <int dim, int spacedim>
8678 template <typename FlagType>
8679 void
8681 {
8682  // The price for not writing ten duplicates of this function is some loss in
8683  // type safety.
8684  if (typeid(flags) == typeid(dx_flags))
8685  dx_flags = *reinterpret_cast<const DataOutBase::DXFlags *>(&flags);
8686  else if (typeid(flags) == typeid(ucd_flags))
8687  ucd_flags = *reinterpret_cast<const DataOutBase::UcdFlags *>(&flags);
8688  else if (typeid(flags) == typeid(povray_flags))
8689  povray_flags = *reinterpret_cast<const DataOutBase::PovrayFlags *>(&flags);
8690  else if (typeid(flags) == typeid(eps_flags))
8691  eps_flags = *reinterpret_cast<const DataOutBase::EpsFlags *>(&flags);
8692  else if (typeid(flags) == typeid(gmv_flags))
8693  gmv_flags = *reinterpret_cast<const DataOutBase::GmvFlags *>(&flags);
8694  else if (typeid(flags) == typeid(tecplot_flags))
8695  tecplot_flags =
8696  *reinterpret_cast<const DataOutBase::TecplotFlags *>(&flags);
8697  else if (typeid(flags) == typeid(vtk_flags))
8698  vtk_flags = *reinterpret_cast<const DataOutBase::VtkFlags *>(&flags);
8699  else if (typeid(flags) == typeid(svg_flags))
8700  svg_flags = *reinterpret_cast<const DataOutBase::SvgFlags *>(&flags);
8701  else if (typeid(flags) == typeid(gnuplot_flags))
8702  gnuplot_flags =
8703  *reinterpret_cast<const DataOutBase::GnuplotFlags *>(&flags);
8704  else if (typeid(flags) == typeid(deal_II_intermediate_flags))
8705  deal_II_intermediate_flags =
8706  *reinterpret_cast<const DataOutBase::Deal_II_IntermediateFlags *>(&flags);
8707  else
8708  Assert(false, ExcNotImplemented());
8709 }
8710 
8711 
8712 
8713 template <int dim, int spacedim>
8714 std::string
8716  const DataOutBase::OutputFormat output_format) const
8717 {
8718  if (output_format == DataOutBase::default_format)
8719  return DataOutBase::default_suffix(default_fmt);
8720  else
8721  return DataOutBase::default_suffix(output_format);
8722 }
8723 
8724 
8725 
8726 template <int dim, int spacedim>
8727 void
8729 {
8730  prm.declare_entry("Output format",
8731  "gnuplot",
8733  "A name for the output format to be used");
8734  prm.declare_entry("Subdivisions",
8735  "1",
8737  "Number of subdivisions of each mesh cell");
8738 
8739  prm.enter_subsection("DX output parameters");
8741  prm.leave_subsection();
8742 
8743  prm.enter_subsection("UCD output parameters");
8745  prm.leave_subsection();
8746 
8747  prm.enter_subsection("Gnuplot output parameters");
8749  prm.leave_subsection();
8750 
8751  prm.enter_subsection("Povray output parameters");
8753  prm.leave_subsection();
8754 
8755  prm.enter_subsection("Eps output parameters");
8757  prm.leave_subsection();
8758 
8759  prm.enter_subsection("Gmv output parameters");
8761  prm.leave_subsection();
8762 
8763  prm.enter_subsection("Tecplot output parameters");
8765  prm.leave_subsection();
8766 
8767  prm.enter_subsection("Vtk output parameters");
8769  prm.leave_subsection();
8770 
8771 
8772  prm.enter_subsection("deal.II intermediate output parameters");
8774  prm.leave_subsection();
8775 }
8776 
8777 
8778 
8779 template <int dim, int spacedim>
8780 void
8782 {
8783  const std::string &output_name = prm.get("Output format");
8784  default_fmt = DataOutBase::parse_output_format(output_name);
8785  default_subdivisions = prm.get_integer("Subdivisions");
8786 
8787  prm.enter_subsection("DX output parameters");
8788  dx_flags.parse_parameters(prm);
8789  prm.leave_subsection();
8790 
8791  prm.enter_subsection("UCD output parameters");
8792  ucd_flags.parse_parameters(prm);
8793  prm.leave_subsection();
8794 
8795  prm.enter_subsection("Gnuplot output parameters");
8796  gnuplot_flags.parse_parameters(prm);
8797  prm.leave_subsection();
8798 
8799  prm.enter_subsection("Povray output parameters");
8800  povray_flags.parse_parameters(prm);
8801  prm.leave_subsection();
8802 
8803  prm.enter_subsection("Eps output parameters");
8804  eps_flags.parse_parameters(prm);
8805  prm.leave_subsection();
8806 
8807  prm.enter_subsection("Gmv output parameters");
8808  gmv_flags.parse_parameters(prm);
8809  prm.leave_subsection();
8810 
8811  prm.enter_subsection("Tecplot output parameters");
8812  tecplot_flags.parse_parameters(prm);
8813  prm.leave_subsection();
8814 
8815  prm.enter_subsection("Vtk output parameters");
8816  vtk_flags.parse_parameters(prm);
8817  prm.leave_subsection();
8818 
8819  prm.enter_subsection("deal.II intermediate output parameters");
8820  deal_II_intermediate_flags.parse_parameters(prm);
8821  prm.leave_subsection();
8822 }
8823 
8824 
8825 
8826 template <int dim, int spacedim>
8827 std::size_t
8829 {
8830  return (sizeof(default_fmt) +
8833  MemoryConsumption::memory_consumption(gnuplot_flags) +
8837  MemoryConsumption::memory_consumption(tecplot_flags) +
8840  MemoryConsumption::memory_consumption(deal_II_intermediate_flags));
8841 }
8842 
8843 
8844 
8845 template <int dim, int spacedim>
8846 std::vector<
8847  std::tuple<unsigned int,
8848  unsigned int,
8849  std::string,
8852 {
8853  return std::vector<
8854  std::tuple<unsigned int,
8855  unsigned int,
8856  std::string,
8858 }
8859 
8860 
8861 template <int dim, int spacedim>
8862 void
8864 {
8865 #ifdef DEBUG
8866  {
8867  // Check that names for datasets are only used once. This is somewhat
8868  // complicated, because vector ranges might have a name or not.
8869  std::set<std::string> all_names;
8870 
8871  const std::vector<
8872  std::tuple<unsigned int,
8873  unsigned int,
8874  std::string,
8876  ranges = this->get_nonscalar_data_ranges();
8877  const std::vector<std::string> data_names = this->get_dataset_names();
8878  const unsigned int n_data_sets = data_names.size();
8879  std::vector<bool> data_set_written(n_data_sets, false);
8880 
8881  for (const auto &range : ranges)
8882  {
8883  const std::string &name = std::get<2>(range);
8884  if (!name.empty())
8885  {
8886  Assert(all_names.find(name) == all_names.end(),
8887  ExcMessage(
8888  "Error: names of fields in DataOut need to be unique, "
8889  "but '" +
8890  name + "' is used more than once."));
8891  all_names.insert(name);
8892  for (unsigned int i = std::get<0>(range); i <= std::get<1>(range);
8893  ++i)
8894  data_set_written[i] = true;
8895  }
8896  }
8897 
8898  for (unsigned int data_set = 0; data_set < n_data_sets; ++data_set)
8899  if (data_set_written[data_set] == false)
8900  {
8901  const std::string &name = data_names[data_set];
8902  Assert(all_names.find(name) == all_names.end(),
8903  ExcMessage(
8904  "Error: names of fields in DataOut need to be unique, "
8905  "but '" +
8906  name + "' is used more than once."));
8907  all_names.insert(name);
8908  }
8909  }
8910 #endif
8911 }
8912 
8913 
8914 
8915 // ---------------------------------------------- DataOutReader ----------
8916 
8917 template <int dim, int spacedim>
8918 void
8920 {
8921  AssertThrow(in.fail() == false, ExcIO());
8922 
8923  // first empty previous content
8924  {
8925  std::vector<typename ::DataOutBase::Patch<dim, spacedim>> tmp;
8926  tmp.swap(patches);
8927  }
8928  {
8929  std::vector<std::string> tmp;
8930  tmp.swap(dataset_names);
8931  }
8932  {
8933  std::vector<
8934  std::tuple<unsigned int,
8935  unsigned int,
8936  std::string,
8938  tmp;
8939  tmp.swap(nonscalar_data_ranges);
8940  }
8941 
8942  // then check that we have the correct header of this file. both the first and
8943  // second real lines have to match, as well as the dimension information
8944  // written before that and the Version information written in the third line
8945  {
8946  std::pair<unsigned int, unsigned int> dimension_info =
8948  AssertThrow((dimension_info.first == dim) &&
8949  (dimension_info.second == spacedim),
8950  ExcIncompatibleDimensions(
8951  dimension_info.first, dim, dimension_info.second, spacedim));
8952 
8953  // read to the end of the line
8954  std::string tmp;
8955  getline(in, tmp);
8956  }
8957 
8958  {
8959  std::string header;
8960  getline(in, header);
8961 
8962  std::ostringstream s;
8963  s << "[deal.II intermediate format graphics data]";
8964 
8965  Assert(header == s.str(), ExcUnexpectedInput(s.str(), header));
8966  }
8967  {
8968  std::string header;
8969  getline(in, header);
8970 
8971  std::ostringstream s;
8972  s << "[written by " << DEAL_II_PACKAGE_NAME << " "
8973  << DEAL_II_PACKAGE_VERSION << "]";
8974 
8975  Assert(header == s.str(), ExcUnexpectedInput(s.str(), header));
8976  }
8977  {
8978  std::string header;
8979  getline(in, header);
8980 
8981  std::ostringstream s;
8982  s << "[Version: "
8984 
8985  Assert(header == s.str(),
8986  ExcMessage(
8987  "Invalid or incompatible file format. Intermediate format "
8988  "files can only be read by the same deal.II version as they "
8989  "are written by."));
8990  }
8991 
8992  // then read the rest of the data
8993  unsigned int n_datasets;
8994  in >> n_datasets;
8995  dataset_names.resize(n_datasets);
8996  for (unsigned int i = 0; i < n_datasets; ++i)
8997  in >> dataset_names[i];
8998 
8999  unsigned int n_patches;
9000  in >> n_patches;
9001  patches.resize(n_patches);
9002  for (unsigned int i = 0; i < n_patches; ++i)
9003  in >> patches[i];
9004 
9005  unsigned int n_nonscalar_data_ranges;
9006  in >> n_nonscalar_data_ranges;
9007  nonscalar_data_ranges.resize(n_nonscalar_data_ranges);
9008  for (unsigned int i = 0; i < n_nonscalar_data_ranges; ++i)
9009  {
9010  in >> std::get<0>(nonscalar_data_ranges[i]) >>
9011  std::get<1>(nonscalar_data_ranges[i]);
9012 
9013  // read in the name of that vector range. because it is on a separate
9014  // line, we first need to read to the end of the previous line (nothing
9015  // should be there any more after we've read the previous two integers)
9016  // and then read the entire next line for the name
9017  std::string name;
9018  getline(in, name);
9019  getline(in, name);
9020  std::get<2>(nonscalar_data_ranges[i]) = name;
9021  }
9022 
9023  AssertThrow(in.fail() == false, ExcIO());
9024 }
9025 
9026 
9027 
9028 template <int dim, int spacedim>
9029 void
9031 {
9032  using Patch = typename ::DataOutBase::Patch<dim, spacedim>;
9033 
9034 
9035  const std::vector<Patch> &source_patches = source.get_patches();
9036  Assert(patches.size() != 0, DataOutBase::ExcNoPatches());
9037  Assert(source_patches.size() != 0, DataOutBase::ExcNoPatches());
9038  // check equality of component names
9039  Assert(get_dataset_names() == source.get_dataset_names(),
9041 
9042  // check equality of the vector data specifications
9043  Assert(get_nonscalar_data_ranges().size() ==
9044  source.get_nonscalar_data_ranges().size(),
9045  ExcMessage("Both sources need to declare the same components "
9046  "as vectors."));
9047  for (unsigned int i = 0; i < get_nonscalar_data_ranges().size(); ++i)
9048  {
9049  Assert(std::get<0>(get_nonscalar_data_ranges()[i]) ==
9050  std::get<0>(source.get_nonscalar_data_ranges()[i]),
9051  ExcMessage("Both sources need to declare the same components "
9052  "as vectors."));
9053  Assert(std::get<1>(get_nonscalar_data_ranges()[i]) ==
9054  std::get<1>(source.get_nonscalar_data_ranges()[i]),
9055  ExcMessage("Both sources need to declare the same components "
9056  "as vectors."));
9057  Assert(std::get<2>(get_nonscalar_data_ranges()[i]) ==
9058  std::get<2>(source.get_nonscalar_data_ranges()[i]),
9059  ExcMessage("Both sources need to declare the same components "
9060  "as vectors."));
9061  }
9062 
9063  // make sure patches are compatible
9064  Assert(patches[0].n_subdivisions == source_patches[0].n_subdivisions,
9066  Assert(patches[0].data.n_rows() == source_patches[0].data.n_rows(),
9068  Assert(patches[0].data.n_cols() == source_patches[0].data.n_cols(),
9070 
9071  // merge patches. store old number of elements, since we need to adjust patch
9072  // numbers, etc afterwards
9073  const unsigned int old_n_patches = patches.size();
9074  patches.insert(patches.end(), source_patches.begin(), source_patches.end());
9075 
9076  // adjust patch numbers
9077  for (unsigned int i = old_n_patches; i < patches.size(); ++i)
9078  patches[i].patch_index += old_n_patches;
9079 
9080  // adjust patch neighbors
9081  for (unsigned int i = old_n_patches; i < patches.size(); ++i)
9082  for (unsigned int n : GeometryInfo<dim>::face_indices())
9083  if (patches[i].neighbors[n] !=
9085  patches[i].neighbors[n] += old_n_patches;
9086 }
9087 
9088 
9089 
9090 template <int dim, int spacedim>
9091 const std::vector<typename ::DataOutBase::Patch<dim, spacedim>> &
9093 {
9094  return patches;
9095 }
9096 
9097 
9098 
9099 template <int dim, int spacedim>
9100 std::vector<std::string>
9102 {
9103  return dataset_names;
9104 }
9105 
9106 
9107 
9108 template <int dim, int spacedim>
9109 std::vector<
9110  std::tuple<unsigned int,
9111  unsigned int,
9112  std::string,
9115 {
9116  return nonscalar_data_ranges;
9117 }
9118 
9119 
9120 
9121 // ---------------------------------------------- XDMFEntry ----------
9122 
9124  : valid(false)
9125  , h5_sol_filename("")
9126  , h5_mesh_filename("")
9127  , entry_time(0.0)
9128  , num_nodes(numbers::invalid_unsigned_int)
9129  , num_cells(numbers::invalid_unsigned_int)
9130  , dimension(numbers::invalid_unsigned_int)
9131  , space_dimension(numbers::invalid_unsigned_int)
9132 {}
9133 
9134 
9135 
9136 XDMFEntry::XDMFEntry(const std::string & filename,
9137  const double time,
9138  const std::uint64_t nodes,
9139  const std::uint64_t cells,
9140  const unsigned int dim)
9141  : XDMFEntry(filename, filename, time, nodes, cells, dim, dim)
9142 {}
9143 
9144 
9145 
9146 XDMFEntry::XDMFEntry(const std::string & mesh_filename,
9147  const std::string & solution_filename,
9148  const double time,
9149  const std::uint64_t nodes,
9150  const std::uint64_t cells,
9151  const unsigned int dim)
9152  : XDMFEntry(mesh_filename, solution_filename, time, nodes, cells, dim, dim)
9153 {}
9154 
9155 
9156 
9157 XDMFEntry::XDMFEntry(const std::string & mesh_filename,
9158  const std::string & solution_filename,
9159  const double time,
9160  const std::uint64_t nodes,
9161  const std::uint64_t cells,
9162  const unsigned int dim,
9163  const unsigned int spacedim)
9164  : valid(true)
9165  , h5_sol_filename(solution_filename)
9166  , h5_mesh_filename(mesh_filename)
9167  , entry_time(time)
9168  , num_nodes(nodes)
9169  , num_cells(cells)
9170  , dimension(dim)
9171  , space_dimension(spacedim)
9172 {}
9173 
9174 
9175 
9176 void
9177 XDMFEntry::add_attribute(const std::string &attr_name,
9178  const unsigned int dimension)
9179 {
9180  attribute_dims[attr_name] = dimension;
9181 }
9182 
9183 
9184 
9185 namespace
9186 {
9190  std::string
9191  indent(const unsigned int indent_level)
9192  {
9193  std::string res = "";
9194  for (unsigned int i = 0; i < indent_level; ++i)
9195  res += " ";
9196  return res;
9197  }
9198 } // namespace
9199 
9200 
9201 
9202 std::string
9203 XDMFEntry::get_xdmf_content(const unsigned int indent_level) const
9204 {
9205  switch (dimension)
9206  {
9207  case 0:
9208  return get_xdmf_content(indent_level,
9209  ReferenceCells::get_hypercube<0>());
9210  case 1:
9211  return get_xdmf_content(indent_level,
9212  ReferenceCells::get_hypercube<1>());
9213  case 2:
9214  return get_xdmf_content(indent_level,
9215  ReferenceCells::get_hypercube<2>());
9216  case 3:
9217  return get_xdmf_content(indent_level,
9218  ReferenceCells::get_hypercube<3>());
9219  default:
9220  Assert(false, ExcNotImplemented());
9221  }
9222 
9223  return "";
9224 }
9225 
9226 
9227 
9228 std::string
9229 XDMFEntry::get_xdmf_content(const unsigned int indent_level,
9230  const ReferenceCell &reference_cell) const
9231 {
9232  if (!valid)
9233  return "";
9234 
9235  std::stringstream ss;
9236  ss << indent(indent_level + 0)
9237  << "<Grid Name=\"mesh\" GridType=\"Uniform\">\n";
9238  ss << indent(indent_level + 1) << "<Time Value=\"" << entry_time << "\"/>\n";
9239  ss << indent(indent_level + 1) << "<Geometry GeometryType=\""
9240  << (space_dimension <= 2 ? "XY" : "XYZ") << "\">\n";
9241  ss << indent(indent_level + 2) << "<DataItem Dimensions=\"" << num_nodes
9242  << " " << (space_dimension <= 2 ? 2 : space_dimension)
9243  << "\" NumberType=\"Float\" Precision=\"8\" Format=\"HDF\">\n";
9244  ss << indent(indent_level + 3) << h5_mesh_filename << ":/nodes\n";
9245  ss << indent(indent_level + 2) << "</DataItem>\n";
9246  ss << indent(indent_level + 1) << "</Geometry>\n";
9247 
9248  // If we have cells defined, use the topology corresponding to the dimension
9249  if (num_cells > 0)
9250  {
9251  ss << indent(indent_level + 1) << "<Topology TopologyType=\"";
9252 
9253  if (dimension == 0)
9254  {
9255  ss << "Polyvertex";
9256  }
9257  else if (dimension == 1)
9258  {
9259  ss << "Polyline";
9260  }
9261  else if (dimension == 2)
9262  {
9265  ExcNotImplemented());
9266 
9268  {
9269  ss << "Quadrilateral";
9270  }
9271  else // if (reference_cell == ReferenceCells::Triangle)
9272  {
9273  ss << "Triangle";
9274  }
9275  }
9276  else if (dimension == 3)
9277  {
9280  ExcNotImplemented());
9281 
9283  {
9284  ss << "Hexahedron";
9285  }
9286  else // if (reference_cell == ReferenceCells::Tetrahedron)
9287  {
9288  ss << "Tetrahedron";
9289  }
9290  }
9291 
9292  ss << "\" NumberOfElements=\"" << num_cells;
9293  if (dimension == 0)
9294  ss << "\" NodesPerElement=\"1\">\n";
9295  else if (dimension == 1)
9296  ss << "\" NodesPerElement=\"2\">\n";
9297  else
9298  // no "NodesPerElement" for dimension 2 and higher
9299  ss << "\">\n";
9300 
9301  ss << indent(indent_level + 2) << "<DataItem Dimensions=\"" << num_cells
9302  << " " << reference_cell.n_vertices()
9303  << "\" NumberType=\"UInt\" Format=\"HDF\">\n";
9304 
9305  ss << indent(indent_level + 3) << h5_mesh_filename << ":/cells\n";
9306  ss << indent(indent_level + 2) << "</DataItem>\n";
9307  ss << indent(indent_level + 1) << "</Topology>\n";
9308  }
9309  // Otherwise, we assume the points are isolated in space and use a Polyvertex
9310  // topology
9311  else
9312  {
9313  ss << indent(indent_level + 1)
9314  << "<Topology TopologyType=\"Polyvertex\" NumberOfElements=\""
9315  << num_nodes << "\">\n";
9316  ss << indent(indent_level + 1) << "</Topology>\n";
9317  }
9318 
9319  for (const auto &attribute_dim : attribute_dims)
9320  {
9321  ss << indent(indent_level + 1) << "<Attribute Name=\""
9322  << attribute_dim.first << "\" AttributeType=\""
9323  << (attribute_dim.second > 1 ? "Vector" : "Scalar")
9324  << "\" Center=\"Node\">\n";
9325  // Vectors must have 3 elements even for 2D models
9326  ss << indent(indent_level + 2) << "<DataItem Dimensions=\"" << num_nodes
9327  << " " << (attribute_dim.second > 1 ? 3 : 1)
9328  << "\" NumberType=\"Float\" Precision=\"8\" Format=\"HDF\">\n";
9329  ss << indent(indent_level + 3) << h5_sol_filename << ":/"
9330  << attribute_dim.first << '\n';
9331  ss << indent(indent_level + 2) << "</DataItem>\n";
9332  ss << indent(indent_level + 1) << "</Attribute>\n";
9333  }
9334 
9335  ss << indent(indent_level + 0) << "</Grid>\n";
9336 
9337  return ss.str();
9338 }
9339 
9340 
9341 
9342 namespace DataOutBase
9343 {
9344  template <int dim, int spacedim>
9345  std::ostream &
9346  operator<<(std::ostream &out, const Patch<dim, spacedim> &patch)
9347  {
9348  // write a header line
9349  out << "[deal.II intermediate Patch<" << dim << ',' << spacedim << ">]"
9350  << '\n';
9351 
9352  // First export what kind of reference cell we are looking at:
9353  out << patch.reference_cell << '\n';
9354 
9355  // then write all the data that is in this patch
9356  for (const unsigned int i : patch.reference_cell.vertex_indices())
9357  out << patch.vertices[i] << ' ';
9358  out << '\n';
9359 
9360  for (unsigned int i : patch.reference_cell.face_indices())
9361  out << patch.neighbors[i] << ' ';
9362  out << '\n';
9363 
9364  out << patch.patch_index << ' ' << patch.n_subdivisions << '\n';
9365 
9366  out << patch.points_are_available << '\n';
9367 
9368  out << patch.data.n_rows() << ' ' << patch.data.n_cols() << '\n';
9369  for (unsigned int i = 0; i < patch.data.n_rows(); ++i)
9370  for (unsigned int j = 0; j < patch.data.n_cols(); ++j)
9371  out << patch.data[i][j] << ' ';
9372  out << '\n';
9373  out << '\n';
9374 
9375  return out;
9376  }
9377 
9378 
9379 
9380  template <int dim, int spacedim>
9381  std::istream &
9382  operator>>(std::istream &in, Patch<dim, spacedim> &patch)
9383  {
9384  AssertThrow(in.fail() == false, ExcIO());
9385 
9386  // read a header line and compare it to what we usually write. skip all
9387  // lines that contain only blanks at the start
9388  {
9389  std::string header;
9390  do
9391  {
9392  getline(in, header);
9393  while ((header.size() != 0) && (header.back() == ' '))
9394  header.erase(header.size() - 1);
9395  }
9396  while ((header.empty()) && in);
9397 
9398  std::ostringstream s;
9399  s << "[deal.II intermediate Patch<" << dim << ',' << spacedim << ">]";
9400 
9401  Assert(header == s.str(), ExcUnexpectedInput(s.str(), header));
9402  }
9403 
9404  // First import what kind of reference cell we are looking at:
9405 #ifdef DEAL_II_HAVE_CXX17
9406  if constexpr (dim > 0)
9407  in >> patch.reference_cell;
9408 #else
9409  // If we can't use 'if constexpr', work around the fact that we can't
9410  // write to a 'const' variable by using a const_cast that is a no-op
9411  // whenever the code is actually executed
9412  if (dim > 0)
9413  in >> const_cast<ReferenceCell &>(patch.reference_cell);
9414 #endif
9415 
9416  // then read all the data that is in this patch
9417  for (const unsigned int i : patch.reference_cell.vertex_indices())
9418  in >> patch.vertices[i];
9419 
9420  for (unsigned int i : patch.reference_cell.face_indices())
9421  in >> patch.neighbors[i];
9422 
9423  in >> patch.patch_index;
9424 
9425  // If dim>1, we also need to set the number of subdivisions, whereas
9426  // in dim==1, this is a const variable equal to one that can't be changed.
9427  unsigned int n_subdivisions;
9428  in >> n_subdivisions;
9429 #ifdef DEAL_II_HAVE_CXX17
9430  if constexpr (dim > 1)
9431  patch.n_subdivisions = n_subdivisions;
9432 #else
9433  // If we can't use 'if constexpr', work around the fact that we can't
9434  // write to a 'const' variable by using a const_cast that is a no-op
9435  // whenever the code is actually executed
9436  if (dim > 1)
9437  const_cast<unsigned int &>(patch.n_subdivisions) = n_subdivisions;
9438 #endif
9439 
9440  in >> patch.points_are_available;
9441 
9442  unsigned int n_rows, n_cols;
9443  in >> n_rows >> n_cols;
9444  patch.data.reinit(n_rows, n_cols);
9445  for (unsigned int i = 0; i < patch.data.n_rows(); ++i)
9446  for (unsigned int j = 0; j < patch.data.n_cols(); ++j)
9447  in >> patch.data[i][j];
9448 
9449  AssertThrow(in.fail() == false, ExcIO());
9450 
9451  return in;
9452  }
9453 } // namespace DataOutBase
9454 
9455 
9456 
9457 // explicit instantiations
9458 #include "data_out_base.inst"
9459 
long int get_integer(const std::string &entry_string) const
bool get_bool(const std::string &entry_name) const
void declare_entry(const std::string &entry, const std::string &default_value, const Patterns::PatternBase &pattern=Patterns::Anything(), const std::string &documentation="", const bool has_to_be_set=false)
std::string get(const std::string &entry_string) const
double get_double(const std::string &entry_name) const
void enter_subsection(const std::string &subsection)
std_cxx20::ranges::iota_view< unsigned int, unsigned int > vertex_indices() const
unsigned int n_vertices() const
std_cxx20::ranges::iota_view< unsigned int, unsigned int > face_indices() const
Definition: tensor.h:503
static constexpr TableIndices< rank_ > unrolled_to_component_indices(const unsigned int i)
constexpr void clear()
void join() const
#define DEAL_II_NAMESPACE_OPEN
Definition: config.h:442
#define DEAL_II_PACKAGE_VERSION
Definition: config.h:26
#define DEAL_II_NAMESPACE_CLOSE
Definition: config.h:443
#define DEAL_II_FALLTHROUGH
Definition: config.h:176
#define DEAL_II_HAVE_CXX17
Definition: config.h:152
#define DEAL_II_PACKAGE_NAME
Definition: config.h:24
Point< 2 > projected_vertices[4]
Point< 3 > center
float depth
Point< 2 > projected_center
Point< 3 > vertices[4]
float color_value
unsigned int level
Definition: grid_out.cc:4606
unsigned int cell_index
Definition: grid_tools.cc:1129
std::size_t memory_consumption() const
static void declare_parameters(ParameterHandler &prm)
virtual std::vector< std::tuple< unsigned int, unsigned int, std::string, DataComponentInterpretation::DataComponentInterpretation > > get_nonscalar_data_ranges() const
const double * get_data_set(const unsigned int set_num) const
static void declare_parameters(ParameterHandler &prm)
std::string write_vtu_with_pvtu_record(const std::string &directory, const std::string &filename_without_extension, const unsigned int counter, const MPI_Comm &mpi_communicator, const unsigned int n_digits_for_counter=numbers::invalid_unsigned_int, const unsigned int n_groups=0) const
static void declare_parameters(ParameterHandler &prm)
void parse_parameters(ParameterHandler &prm)
static ::ExceptionBase & ExcIncompatiblePatchLists()
static RgbValues default_color_function(const double value, const double min_value, const double max_value)
std::map< unsigned int, unsigned int > filtered_points
void write_filtered_data(DataOutBase::DataOutFilter &filtered_data) const
std::ostream & operator<<(std::ostream &out, const Patch< dim, spacedim > &patch)
std::vector< std::string > space_dimension_labels
static ::ExceptionBase & ExcErrorOpeningTecplotFile(char *arg1)
void write_pvtu_record(std::ostream &out, const std::vector< std::string > &piece_names) const
static void declare_parameters(ParameterHandler &prm)
std::size_t memory_consumption() const
void write_hdf5_parallel(const DataOutBase::DataOutFilter &data_filter, const std::string &filename, const MPI_Comm &comm) const
void parse_parameters(const ParameterHandler &prm)
ColorFunction color_function
std::string get_data_set_name(const unsigned int set_num) const
void write_ucd(std::ostream &out) const
static ::ExceptionBase & ExcLowerRange(int arg1, int arg2)
void merge(const DataOutReader< dim, spacedim > &other)
unsigned int patch_index
static ::ExceptionBase & ExcInternalError()
ReferenceCell reference_cell
void write_povray(std::ostream &out) const
RgbValues(*)(const double value, const double min_value, const double max_value) ColorFunction
std::string default_suffix(const DataOutBase::OutputFormat output_format=DataOutBase::default_format) const
Table< 2, float > data
static RgbValues grey_scale_color_function(const double value, const double min_value, const double max_value)
void internal_add_cell(const unsigned int cell_index, const unsigned int pt_index)
static void declare_parameters(ParameterHandler &prm)
void write_cell(const unsigned int index, const unsigned int start, const unsigned int d1, const unsigned int d2, const unsigned int d3)
ZlibCompressionLevel compression_level
VtkFlags(const double time=std::numeric_limits< double >::min(), const unsigned int cycle=std::numeric_limits< unsigned int >::min(), const bool print_date_and_time=true, const ZlibCompressionLevel compression_level=best_compression, const bool write_higher_order_cells=false, const std::map< std::string, std::string > &physical_units={})
static ::ExceptionBase & ExcNotInitialized()
XDMFEntry create_xdmf_entry(const DataOutBase::DataOutFilter &data_filter, const std::string &h5_filename, const double cur_time, const MPI_Comm &comm) const
std::uint64_t num_nodes
static const unsigned int format_version
static const unsigned int no_neighbor
static ::ExceptionBase & ExcFileNotOpen(std::string arg1)
unsigned int dimension
PovrayFlags(const bool smooth=false, const bool bicubic_patch=false, const bool external_data=false)
void write_vtu_in_parallel(const std::string &filename, const MPI_Comm &comm) const
virtual const std::vector<::DataOutBase::Patch< dim, spacedim > > & get_patches() const override
unsigned int height_vector
static ::ExceptionBase & ExcDimensionMismatch(std::size_t arg1, std::size_t arg2)
EpsFlags(const unsigned int height_vector=0, const unsigned int color_vector=0, const SizeType size_type=width, const unsigned int size=300, const double line_width=0.5, const double azimut_angle=60, const double turn_angle=30, const double z_scaling=1.0, const bool draw_mesh=true, const bool draw_cells=true, const bool shade_cells=true, const ColorFunction color_function=&default_color_function)
void parse_parameters(const ParameterHandler &prm)
std::vector< unsigned int > data_set_dims
std::size_t memory_consumption() const
void set_default_format(const DataOutBase::OutputFormat default_format)
unsigned int n_nodes() const
std::map< std::string, std::string > physical_units
#define Assert(cond, exc)
Definition: exceptions.h:1473
void parse_parameters(const ParameterHandler &prm)
unsigned int color_vector
Point< spacedim > vertices[GeometryInfo< dim >::vertices_per_cell]
static ::ExceptionBase & ExcIncompatibleDatasetNames()
void write(std::ostream &out, const DataOutBase::OutputFormat output_format=DataOutBase::default_format) const
static void declare_parameters(ParameterHandler &prm)
static ::ExceptionBase & ExcNotImplemented()
void fill_node_data(std::vector< double > &node_data) const
std::vector< std::vector< double > > data_sets
static ::ExceptionBase & ExcInvalidDatasetSize(int arg1, int arg2)
bool operator==(const Patch &patch) const
std::string h5_sol_filename
static const unsigned int n_subdivisions
unsigned int get_data_set_dim(const unsigned int set_num) const
void write_gnuplot(std::ostream &out) const
void read(std::istream &in)
#define DeclException2(Exception2, type1, type2, outsequence)
Definition: exceptions.h:532
SvgFlags(const unsigned int height_vector=0, const int azimuth_angle=37, const int polar_angle=45, const unsigned int line_thickness=1, const bool margin=true, const bool draw_colorbar=true)
static RgbValues reverse_grey_scale_color_function(const double value, const double min_value, const double max_value)
void write_vtu(std::ostream &out) const
#define AssertDimension(dim1, dim2)
Definition: exceptions.h:1667
static ::ExceptionBase & ExcNotEnoughSpaceDimensionLabels()
#define AssertThrowMPI(error_code)
Definition: exceptions.h:1790
static const unsigned int space_dim
void write_tecplot(std::ostream &out) const
static const ReferenceCell reference_cell
TecplotFlags(const char *zone_name=nullptr, const double solution_time=-1.0)
void write_data_set(const std::string &name, const unsigned int dimension, const unsigned int set_num, const Table< 2, double > &data_vectors)
void parse_parameters(const ParameterHandler &prm)
double entry_time
static ::ExceptionBase & ExcTecplotAPIError()
std::string h5_mesh_filename
std::string get_xdmf_content(const unsigned int indent_level) const
std::map< unsigned int, unsigned int > filtered_cells
void add_attribute(const std::string &attr_name, const unsigned int dimension)
virtual std::vector< std::tuple< unsigned int, unsigned int, std::string, DataComponentInterpretation::DataComponentInterpretation > > get_nonscalar_data_ranges() const override
std::uint64_t num_cells
std::map< std::string, unsigned int > attribute_dims
#define AssertIndexRange(index, range)
Definition: exceptions.h:1732
virtual std::vector< std::string > get_dataset_names() const override
void write_svg(std::ostream &out) const
void write_cell_single(const unsigned int index, const unsigned int start, const unsigned int n_points, const ReferenceCell &reference_cell)
static ::ExceptionBase & ExcIndexRange(std::size_t arg1, std::size_t arg2, std::size_t arg3)
unsigned int height_vector
unsigned int n_subdivisions
void write_xdmf_file(const std::vector< XDMFEntry > &entries, const std::string &filename, const MPI_Comm &comm) const
static ::ExceptionBase & ExcIO()
void validate_dataset_names() const
void set_flags(const FlagType &flags)
std::vector< std::string > data_set_names
void write_vtk(std::ostream &out) const
unsigned int line_thickness
static ::ExceptionBase & ExcNoPatches()
void swap(Patch< dim, spacedim > &other_patch)
void fill_cell_data(const unsigned int local_node_offset, std::vector< unsigned int > &cell_data) const
std::istream & operator>>(std::istream &in, Patch< dim, spacedim > &patch)
DataOutFilterFlags(const bool filter_duplicate_vertices=false, const bool xdmf_hdf5_output=false)
void write_point(const unsigned int index, const Point< dim > &p)
UcdFlags(const bool write_preamble=false)
std::size_t memory_consumption() const
void parse_parameters(const ParameterHandler &prm)
DXFlags(const bool write_neighbors=false, const bool int_binary=false, const bool coordinates_binary=false, const bool data_binary=false)
std::array< unsigned int, GeometryInfo< dim >::faces_per_cell > neighbors
void write_gmv(std::ostream &out) const
static ::ExceptionBase & ExcMessage(std::string arg1)
void write_eps(std::ostream &out) const
unsigned int n_cells() const
unsigned int space_dimension
static void declare_parameters(ParameterHandler &prm)
DataOutBase::DataOutFilterFlags flags
void write_dx(std::ostream &out) const
#define AssertThrow(cond, exc)
Definition: exceptions.h:1583
unsigned int n_data_sets() const
void write_deal_II_intermediate(std::ostream &out) const
@ width
Scale to given width.
@ height
Scale to given height.
Task< RT > new_task(const std::function< RT()> &function)
OutputOperator< VectorType > & operator<<(OutputOperator< VectorType > &out, unsigned int step)
Definition: operator.h:165
std::pair< unsigned int, unsigned int > determine_intermediate_format_dimensions(std::istream &input)
void write_vtk(const std::vector< Patch< dim, spacedim >> &patches, const std::vector< std::string > &data_names, const std::vector< std::tuple< unsigned int, unsigned int, std::string, DataComponentInterpretation::DataComponentInterpretation >> &nonscalar_data_ranges, const VtkFlags &flags, std::ostream &out)
void write_vtu(const std::vector< Patch< dim, spacedim >> &patches, const std::vector< std::string > &data_names, const std::vector< std::tuple< unsigned int, unsigned int, std::string, DataComponentInterpretation::DataComponentInterpretation >> &nonscalar_data_ranges, const VtkFlags &flags, std::ostream &out)
void write_gnuplot(const std::vector< Patch< dim, spacedim >> &patches, const std::vector< std::string > &data_names, const std::vector< std::tuple< unsigned int, unsigned int, std::string, DataComponentInterpretation::DataComponentInterpretation >> &nonscalar_data_ranges, const GnuplotFlags &flags, std::ostream &out)
void write_pvtu_record(std::ostream &out, const std::vector< std::string > &piece_names, const std::vector< std::string > &data_names, const std::vector< std::tuple< unsigned int, unsigned int, std::string, DataComponentInterpretation::DataComponentInterpretation >> &nonscalar_data_ranges, const VtkFlags &flags)
void write_data(const std::vector< Patch< dim, spacedim >> &patches, unsigned int n_data_sets, const bool double_precision, StreamType &out)
void write_filtered_data(const std::vector< Patch< dim, spacedim >> &patches, const std::vector< std::string > &data_names, const std::vector< std::tuple< unsigned int, unsigned int, std::string, DataComponentInterpretation::DataComponentInterpretation >> &nonscalar_data_ranges, DataOutFilter &filtered_data)
void write_ucd(const std::vector< Patch< dim, spacedim >> &patches, const std::vector< std::string > &data_names, const std::vector< std::tuple< unsigned int, unsigned int, std::string, DataComponentInterpretation::DataComponentInterpretation >> &nonscalar_data_ranges, const UcdFlags &flags, std::ostream &out)
void write_vtu_header(std::ostream &out, const VtkFlags &flags)
void write_pvd_record(std::ostream &out, const std::vector< std::pair< double, std::string >> &times_and_names)
void write_deal_II_intermediate(const std::vector< Patch< dim, spacedim >> &patches, const std::vector< std::string > &data_names, const std::vector< std::tuple< unsigned int, unsigned int, std::string, DataComponentInterpretation::DataComponentInterpretation >> &nonscalar_data_ranges, const Deal_II_IntermediateFlags &flags, std::ostream &out)
void write_dx(const std::vector< Patch< dim, spacedim >> &patches, const std::vector< std::string > &data_names, const std::vector< std::tuple< unsigned int, unsigned int, std::string, DataComponentInterpretation::DataComponentInterpretation >> &nonscalar_data_ranges, const DXFlags &flags, std::ostream &out)
void write_tecplot_binary(const std::vector< Patch< dim, spacedim >> &patches, const std::vector< std::string > &data_names, const std::vector< std::tuple< unsigned int, unsigned int, std::string, DataComponentInterpretation::DataComponentInterpretation >> &nonscalar_data_ranges, const TecplotFlags &flags, std::ostream &out)
void write_eps(const std::vector< Patch< 2, spacedim >> &patches, const std::vector< std::string > &data_names, const std::vector< std::tuple< unsigned int, unsigned int, std::string, DataComponentInterpretation::DataComponentInterpretation >> &nonscalar_data_ranges, const EpsFlags &flags, std::ostream &out)
void write_vtu_footer(std::ostream &out)
void write_nodes(const std::vector< Patch< dim, spacedim >> &patches, StreamType &out)
void write_tecplot(const std::vector< Patch< dim, spacedim >> &patches, const std::vector< std::string > &data_names, const std::vector< std::tuple< unsigned int, unsigned int, std::string, DataComponentInterpretation::DataComponentInterpretation >> &nonscalar_data_ranges, const TecplotFlags &flags, std::ostream &out)
OutputFormat parse_output_format(const std::string &format_name)
void write_svg(const std::vector< Patch< dim, spacedim >> &, const std::vector< std::string > &, const std::vector< std::tuple< unsigned int, unsigned int, std::string, DataComponentInterpretation::DataComponentInterpretation >> &, const SvgFlags &, std::ostream &)
void write_vtu_main(const std::vector< Patch< dim, spacedim >> &patches, const std::vector< std::string > &data_names, const std::vector< std::tuple< unsigned int, unsigned int, std::string, DataComponentInterpretation::DataComponentInterpretation >> &nonscalar_data_ranges, const VtkFlags &flags, std::ostream &out)
void write_cells(const std::vector< Patch< dim, spacedim >> &patches, StreamType &out)
void write_svg(const std::vector< Patch< 2, spacedim >> &patches, const std::vector< std::string > &data_names, const std::vector< std::tuple< unsigned int, unsigned int, std::string, DataComponentInterpretation::DataComponentInterpretation >> &nonscalar_data_ranges, const SvgFlags &flags, std::ostream &out)
void write_high_order_cells(const std::vector< Patch< dim, spacedim >> &patches, StreamType &out)
std::string get_output_format_names()
void write_visit_record(std::ostream &out, const std::vector< std::string > &piece_names)
void write_povray(const std::vector< Patch< dim, spacedim >> &patches, const std::vector< std::string > &data_names, const std::vector< std::tuple< unsigned int, unsigned int, std::string, DataComponentInterpretation::DataComponentInterpretation >> &nonscalar_data_ranges, const PovrayFlags &flags, std::ostream &out)
std::string default_suffix(const OutputFormat output_format)
void write_hdf5_parallel(const std::vector< Patch< dim, spacedim >> &patches, const DataOutFilter &data_filter, const std::string &filename, const MPI_Comm &comm)
void write_gmv(const std::vector< Patch< dim, spacedim >> &patches, const std::vector< std::string > &data_names, const std::vector< std::tuple< unsigned int, unsigned int, std::string, DataComponentInterpretation::DataComponentInterpretation >> &nonscalar_data_ranges, const GmvFlags &flags, std::ostream &out)
void reference_cell(Triangulation< dim, spacedim > &tria, const ReferenceCell &reference_cell)
void scale(const double scaling_factor, Triangulation< dim, spacedim > &triangulation)
Definition: grid_tools.cc:2084
@ valid
Iterator points to a valid object.
static const char A
static const char T
types::global_dof_index size_type
Definition: cuda_kernels.h:45
double norm(const FEValuesBase< dim > &fe, const ArrayView< const std::vector< Tensor< 1, dim >>> &Du)
Definition: divergence.h:472
std::enable_if< std::is_fundamental< T >::value, std::size_t >::type memory_consumption(const T &t)
void swap(MemorySpaceData< Number, MemorySpace > &, MemorySpaceData< Number, MemorySpace > &)
Point< spacedim > point(const gp_Pnt &p, const double tolerance=1e-10)
Definition: utilities.cc:190
SymmetricTensor< 2, dim, Number > d(const Tensor< 2, dim, Number > &F, const Tensor< 2, dim, Number > &dF_dt)
SymmetricTensor< 2, dim, Number > epsilon(const Tensor< 2, dim, Number > &Grad_u)
SymmetricTensor< 2, dim, Number > e(const Tensor< 2, dim, Number > &F)
SymmetricTensor< 2, dim, Number > b(const Tensor< 2, dim, Number > &F)
constexpr const ReferenceCell Tetrahedron
constexpr const ReferenceCell Quadrilateral
constexpr const ReferenceCell Wedge
constexpr const ReferenceCell Pyramid
constexpr const ReferenceCell Invalid
constexpr const ReferenceCell Triangle
constexpr const ReferenceCell Hexahedron
constexpr const ReferenceCell Vertex
constexpr const ReferenceCell Line
VectorType::value_type * begin(VectorType &V)
VectorType::value_type * end(VectorType &V)
int File_write_at_c(MPI_File fh, MPI_Offset offset, const void *buf, MPI_Count count, MPI_Datatype datatype, MPI_Status *status)
int File_write_at_all_c(MPI_File fh, MPI_Offset offset, const void *buf, MPI_Count count, MPI_Datatype datatype, MPI_Status *status)
void free_communicator(MPI_Comm &mpi_communicator)
Definition: mpi.cc:194
unsigned int this_mpi_process(const MPI_Comm &mpi_communicator)
Definition: mpi.cc:151
T sum(const T &t, const MPI_Comm &mpi_communicator)
unsigned int n_mpi_processes(const MPI_Comm &mpi_communicator)
Definition: mpi.cc:140
std::string get_time()
Definition: utilities.cc:1016
std::string get_date()
Definition: utilities.cc:1032
constexpr T pow(const T base, const int iexp)
Definition: utilities.h:462
std::string encode_base64(const std::vector< unsigned char > &binary_input)
Definition: utilities.cc:436
std::string int_to_string(const unsigned int value, const unsigned int digits=numbers::invalid_unsigned_int)
Definition: utilities.cc:473
unsigned int needed_digits(const unsigned int max_number)
Definition: utilities.cc:568
unsigned int n_cells(const internal::TriangulationImplementation::NumberCache< 1 > &c)
Definition: tria.cc:13734
void copy(const T *begin, const T *end, U *dest)
static constexpr double PI
Definition: numbers.h:233
static const unsigned int invalid_unsigned_int
Definition: types.h:201
unsigned int global_dof_index
Definition: types.h:76
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)
bool operator<(const SynchronousIterators< Iterators > &a, const SynchronousIterators< Iterators > &b)
const MPI_Comm & comm