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