Loading [MathJax]/extensions/TeX/newcommand.js
 deal.II version GIT relicensing-3087-ga35b476a78 2025-04-19 20:30: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\}}
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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{
65 DeclException2(ExcUnexpectedInput,
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
84 get_zlib_compression_level(const DataOutBase::CompressionLevel level)
85 {
86 switch (level)
87 {
89 return Z_NO_COMPRESSION;
91 return Z_BEST_SPEED;
93 return Z_BEST_COMPRESSION;
95 return Z_DEFAULT_COMPRESSION;
96 default:
98 return Z_NO_COMPRESSION;
99 }
100 }
101
102# ifdef DEAL_II_WITH_MPI
107 int
108 get_boost_zlib_compression_level(const DataOutBase::CompressionLevel level)
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:
147 AssertThrow(uncompressed_size <=
148 std::numeric_limits<std::uint32_t>::max(),
150
151 // allocate a buffer for compressing data and do so
152 auto compressed_data_length = compressBound(uncompressed_size);
153 AssertThrow(compressed_data_length <=
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],
160 &compressed_data_length,
161 reinterpret_cast<const Bytef *>(data.data()),
162 uncompressed_size,
163 get_zlib_compression_level(compression_level));
164 (void)err;
165 Assert(err == Z_OK, ExcInternalError());
166
167 // Discard the unnecessary bytes
168 compressed_data.resize(compressed_data_length);
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)}) +
184 Utilities::encode_base64(compressed_data));
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
239 struct ParallelIntermediateHeader
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
284 Point<2> projected_vertices[4];
285
286 // Center of the cell (projected, two-dimensional)
287 Point<2> projected_center;
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>>
359 create_global_data_table(const std::vector<Patch<dim, spacedim>> &patches)
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,
380 const Patch<dim, spacedim> &patch) {
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)
411 (*global_data_table)[data_set][next_value] =
412 patch.data(data_set, i);
413 }
414 Assert(next_value == n_data_points, ExcInternalError());
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
446 Point<3> int_pt;
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 {
457 internal_ind = existing_points.size();
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
465 filtered_points[index] = internal_ind;
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
499 DataOutFilter::fill_cell_data(const unsigned int local_node_offset,
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] =
507 filtered_cell.second + local_node_offset;
508 }
509 }
510
511
512
513 std::string
514 DataOutFilter::get_data_set_name(const unsigned int set_num) const
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)
650 internal_add_cell(base_entry + 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,
662 const Table<2, double> &data_vectors)
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>
723 extract_vtk_patch_info(const DataOutBase::Patch<dim, spacedim> &patch,
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 {
744 vtk_cell_id[0] = patch.reference_cell.vtk_quadratic_type();
745 vtk_cell_id[2] = patch.data.n_cols();
746 }
748 patch.data.n_cols() == 10)
749 {
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>
784 get_equispaced_location(
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
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 }
826 Point<spacedim> node;
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 constexpr (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
843 Point<spacedim> node =
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>
875 get_node_location(const DataOutBase::Patch<dim, spacedim> &patch,
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;
883 {
885
886 static const std::array<unsigned int, 5> table = {{0, 1, 3, 2, 4}};
887 point_no_actual = table[node_index];
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 {
894 Point<spacedim> node;
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 {
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>
923 count_nodes_and_cells(
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 {
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 {
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>
961 count_nodes_and_cells_and_points(
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;
981 n_points_and_n_cells +=
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);
992 n_cells += n_subcells;
993 n_points_and_n_cells +=
994 n_subcells * (1 + GeometryInfo<dim>::vertices_per_cell);
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)
1422 temp[i] = nodes[GeometryInfo<dim>::dx_to_deal[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';
1430 stream << nodes[GeometryInfo<dim>::dx_to_deal[nodes.size() - 1]]
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 {
1682 stream << GeometryInfo<dim>::vertices_per_cell << '\t';
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 {
2292 EpsFlags::RgbValues rgb_values;
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 {
2305 EpsFlags::RgbValues rgb_values;
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
2447 parse_output_format(const std::string &format_name)
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
2504 default_suffix(const OutputFormat output_format)
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,
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)
2588 node_positions.emplace_back(get_equispaced_location(
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)
2595 node_positions.emplace_back(get_equispaced_location(
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++,
2639 first_vertex_of_patch,
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 =
2665 first_vertex_of_patch + i1 * d1;
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 =
2681 first_vertex_of_patch + i2 * d2 + i1 * d1;
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
2715 first_vertex_of_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 {
2731 Assert(dim <= 3 && dim > 1, ExcNotImplemented());
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
2745 out.template write_high_order_cell<dim>(first_vertex_of_patch,
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
2773 .template vtk_lexicographic_to_node_index<1>(
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
2788 .template vtk_lexicographic_to_node_index<2>(
2789 {{i1, i2}},
2790 {{n_subdivisions, n_subdivisions}},
2791 legacy_format);
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
2807 .template vtk_lexicographic_to_node_index<3>(
2808 {{i1, i2, i3}},
2809 {{n_subdivisions,
2810 n_subdivisions,
2811 n_subdivisions}},
2812 legacy_format);
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:
2824 out.template write_high_order_cell<dim>(first_vertex_of_patch,
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),
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>
2898 svg_project_point(Point<3> point,
2899 Point<3> camera_position,
2900 Point<3> camera_direction,
2901 Point<3> camera_horizontal,
2902 float camera_focus)
2903 {
2904 Point<3> camera_vertical;
2905 camera_vertical[0] = camera_horizontal[1] * camera_direction[2] -
2906 camera_horizontal[2] * camera_direction[1];
2907 camera_vertical[1] = camera_horizontal[2] * camera_direction[0] -
2908 camera_horizontal[0] * camera_direction[2];
2909 camera_vertical[2] = camera_horizontal[0] * camera_direction[1] -
2910 camera_horizontal[1] * camera_direction[0];
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
2918 Point<3> projection;
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
2926 Point<2> projection_decomposition;
2927 projection_decomposition[0] = (projection[0] - camera_position[0] -
2928 camera_focus * camera_direction[0]) *
2929 camera_horizontal[0];
2930 projection_decomposition[0] += (projection[1] - camera_position[1] -
2931 camera_focus * camera_direction[1]) *
2932 camera_horizontal[1];
2933 projection_decomposition[0] += (projection[2] - camera_position[2] -
2934 camera_focus * camera_direction[2]) *
2935 camera_horizontal[2];
2936
2937 projection_decomposition[1] = (projection[0] - camera_position[0] -
2938 camera_focus * camera_direction[0]) *
2939 camera_vertical[0];
2940 projection_decomposition[1] += (projection[1] - camera_position[1] -
2941 camera_focus * camera_direction[1]) *
2942 camera_vertical[1];
2943 projection_decomposition[1] += (projection[2] - camera_position[2] -
2944 camera_focus * camera_direction[2]) *
2945 camera_vertical[2];
2946
2947 return projection_decomposition;
2948 }
2950
2955 Point<6>
2956 svg_get_gradient_parameters(Point<3> points[])
2957 {
2958 Point<3> v_min, v_max, v_inter;
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
3164 Point<6> gradient_parameters;
3165
3166 gradient_parameters[0] = v_min[0];
3167 gradient_parameters[1] = v_min[1];
3168
3169 gradient_parameters[2] = v_min[0] + lambda * gradient[0];
3170 gradient_parameters[3] = v_min[1] + lambda * gradient[1];
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 constexpr (dim == 1)
3353 out << "lines";
3354 else if constexpr (dim == 2)
3355 out << "quads";
3356 else if constexpr (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),
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 << ' ';
3652 output_point_data(0);
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
3673 output_point_data(i1);
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>(
3687 n_points_per_direction),
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
3697 out << get_equispaced_location(patch,
3698 {i1, i2},
3699 n_subdivisions)
3700 << ' ';
3701
3702 output_point_data(i1 + i2 * n_points_per_direction);
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) << ' ';
3728 output_point_data(0);
3729 out << '\n';
3730
3731 out << get_node_location(patch, 1) << ' ';
3732 output_point_data(1);
3733 out << '\n';
3734 out << '\n'; // end of one row of points
3735
3736 out << get_node_location(patch, 2) << ' ';
3737 output_point_data(2);
3738 out << '\n';
3739
3740 out << get_node_location(patch, 2) << ' ';
3741 output_point_data(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>(
3762 n_points_per_direction),
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
3776 const Point<spacedim> this_point =
3777 get_equispaced_location(patch,
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 << ' ';
3785 output_point_data(i1 +
3786 i2 * n_points_per_direction +
3787 i3 * n_points_per_direction *
3788 n_points_per_direction);
3789 out << '\n';
3790
3791 // write point there and its data
3792 out << get_equispaced_location(patch,
3793 {i1 + 1, i2, i3},
3794 n_subdivisions)
3795 << ' ';
3796
3797 output_point_data((i1 + 1) +
3798 i2 * n_points_per_direction +
3799 i3 * n_points_per_direction *
3800 n_points_per_direction);
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 << ' ';
3812 output_point_data(i1 +
3813 i2 * n_points_per_direction +
3814 i3 * n_points_per_direction *
3815 n_points_per_direction);
3816 out << '\n';
3817
3818 // write point there and its data
3819 out << get_equispaced_location(patch,
3820 {i1, i2 + 1, i3},
3821 n_subdivisions)
3822 << ' ';
3823
3824 output_point_data(
3825 i1 + (i2 + 1) * n_points_per_direction +
3826 i3 * n_points_per_direction *
3827 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 << ' ';
3839 output_point_data(i1 +
3840 i2 * n_points_per_direction +
3841 i3 * n_points_per_direction *
3842 n_points_per_direction);
3843 out << '\n';
3844
3845 // write point there and its data
3846 out << get_equispaced_location(patch,
3847 {i1, i2, i3 + 1},
3848 n_subdivisions)
3849 << ' ';
3850
3851 output_point_data(
3852 i1 + i2 * n_points_per_direction +
3853 (i3 + 1) * n_points_per_direction *
3854 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) << ' ';
3869 output_point_data(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) << ' ';
3877 output_point_data(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) << ' ';
3890 output_point_data(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) << ' ';
3898 output_point_data(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) << ' ';
3915 output_point_data(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) << ' ';
3923 output_point_data(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) << ' ';
3931 output_point_data(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),
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),
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.
4452 EpsCell2d eps_cell;
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
4490 const Point<spacedim> center_point =
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
4530 min_color_value =
4531 std::min(min_color_value, eps_cell.color_value);
4532 max_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
4604 if (max_color_value == min_color_value)
4605 max_color_value = min_color_value + 1;
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 {
4615 const EpsFlags::RgbValues rgb_values =
4616 (*flags.color_function)(cell.color_value,
4617 min_color_value,
4618 max_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.
4721 create_global_data_table_task = Threads::new_task(
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:
4754 const Table<2, double> data_vectors =
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.
4896 create_global_data_table_task = Threads::new_task(
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:
4915 const Table<2, double> data_vectors =
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) =
5024 count_nodes_and_cells_and_points(patches, flags.write_higher_order_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.
5038 create_global_data_table_task = Threads::new_task(
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 =
5065 extract_vtk_patch_info(patch, flags.write_higher_order_cells);
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:
5076 const Table<2, double> data_vectors =
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),
5097 ExcLowerRange(std::get<1>(nonscalar_data_range),
5098 std::get<0>(nonscalar_data_range)));
5099 AssertThrow(std::get<1>(nonscalar_data_range) < n_data_sets,
5100 ExcIndexRange(std::get<1>(nonscalar_data_range),
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);
5112 i <= std::get<1>(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);
5125 i < std::get<1>(nonscalar_data_range);
5126 ++i)
5127 out << data_names[i] << "__";
5128 out << data_names[std::get<1>(nonscalar_data_range)];
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:
5140 out << data_vectors(std::get<0>(nonscalar_data_range), n)
5141 << " 0 0" << '\n';
5142 break;
5143
5144 case 1:
5145 out << data_vectors(std::get<0>(nonscalar_data_range), n)
5146 << ' '
5147 << data_vectors(std::get<0>(nonscalar_data_range) + 1, n)
5148 << " 0" << '\n';
5149 break;
5150 case 2:
5151 out << data_vectors(std::get<0>(nonscalar_data_range), n)
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);
5310 i <= std::get<1>(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);
5323 i < std::get<1>(nonscalar_data_range);
5324 ++i)
5325 out << data_names[i] << "__";
5326 out << data_names[std::get<1>(nonscalar_data_range)];
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 =
5392 (deal_ii_with_zlib &&
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) =
5402 count_nodes_and_cells_and_points(patches, flags.write_higher_order_cells);
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,
5411 output_precision =
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 }
5433 o << vtu_stringize_array(node_coordinates_3d,
5434 flags.compression_level,
5435 output_precision)
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,
5449 ascii_or_binary,
5450 output_precision =
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<std::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 {
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(
5511 first_vertex_of_patch +
5513 }
5514 else
5515 {
5516 for (unsigned int i = 0; i < n_points; ++i)
5517 o << '\t'
5518 << (first_vertex_of_patch +
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,
5537 first_vertex_of_patch,
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
5553 local_vertex_order.clear();
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);
5565 flush_current_cell();
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);
5577 flush_current_cell();
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 =
5588 i2 * n_points_per_direction + i1;
5589 local_vertex_order.emplace_back(
5590 starting_offset);
5591 local_vertex_order.emplace_back(
5592 starting_offset + 1);
5593 local_vertex_order.emplace_back(
5594 starting_offset + n_points_per_direction + 1);
5595 local_vertex_order.emplace_back(
5596 starting_offset + n_points_per_direction);
5597 flush_current_cell();
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 =
5610 i3 * n_points_per_direction *
5611 n_points_per_direction +
5612 i2 * n_points_per_direction + i1;
5613 local_vertex_order.emplace_back(
5614 starting_offset);
5615 local_vertex_order.emplace_back(
5616 starting_offset + 1);
5617 local_vertex_order.emplace_back(
5618 starting_offset + n_points_per_direction +
5619 1);
5620 local_vertex_order.emplace_back(
5621 starting_offset + n_points_per_direction);
5622 local_vertex_order.emplace_back(
5623 starting_offset + n_points_per_direction *
5624 n_points_per_direction);
5625 local_vertex_order.emplace_back(
5626 starting_offset +
5627 n_points_per_direction *
5628 n_points_per_direction +
5629 1);
5630 local_vertex_order.emplace_back(
5631 starting_offset +
5632 n_points_per_direction *
5633 n_points_per_direction +
5634 n_points_per_direction + 1);
5635 local_vertex_order.emplace_back(
5636 starting_offset +
5637 n_points_per_direction *
5638 n_points_per_direction +
5639 n_points_per_direction);
5640 flush_current_cell();
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
5672 .template vtk_lexicographic_to_node_index<1>(
5673 {{i1}},
5674 {{n_subdivisions}},
5675 /* use VTU, not VTK: */ false);
5676 local_vertex_order[connectivity_index] =
5677 local_index;
5678 flush_current_cell();
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 =
5691 i2 * n_points_per_direction + i1;
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);
5698 local_vertex_order[connectivity_index] =
5699 local_index;
5700 }
5701 flush_current_cell();
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 =
5715 i3 * n_points_per_direction *
5716 n_points_per_direction +
5717 i2 * n_points_per_direction + i1;
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);
5726 local_vertex_order[connectivity_index] =
5727 local_index;
5728 }
5729
5730 flush_current_cell();
5731 break;
5732 }
5733 default:
5735 }
5736 }
5737
5738 // Finally update the number of the first vertex of this
5739 // patch
5740 first_vertex_of_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,
5751 output_precision)
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.
5774 const auto stringize_cell_offset_and_type_information =
5775 [&patches,
5776 &flags,
5777 ascii_or_binary,
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<std::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 =
5798 extract_vtk_patch_info(patch, flags.write_higher_order_cells);
5799
5800 for (unsigned int i = 0; i < vtk_cell_id[1]; ++i)
5801 {
5802 cell_types.push_back(vtk_cell_id[0]);
5803 first_vertex_of_patch += vtk_cell_id[2];
5804 offsets.push_back(first_vertex_of_patch);
5805 }
5806 }
5807
5808 o << vtu_stringize_array(offsets,
5809 flags.compression_level,
5810 output_precision);
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<std::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
5824 o << vtu_stringize_array(cell_types_uint8_t,
5825 flags.compression_level,
5826 output_precision);
5827 }
5828 else
5829 {
5830 o << vtu_stringize_array(cell_types,
5831 flags.compression_level,
5832 output_precision);
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
5846 const auto stringize_nonscalar_data_range =
5847 [&flags,
5848 &data_names,
5849 ascii_or_binary,
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] << "__";
5892 o << data_names[last_component];
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 {
5949 Tensor<2, 3> vtk_data;
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
5994 o << vtu_stringize_array(data,
5995 flags.compression_level,
5996 output_precision);
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,
6006 ascii_or_binary,
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());
6023 o << vtu_stringize_array(data,
6024 flags.compression_level,
6025 output_precision);
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.
6045 create_global_data_table_task = Threads::new_task([&patches]() {
6046 return create_global_data_table<dim, spacedim, float>(patches);
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:
6053 mesh_tasks += Threads::new_task(stringize_vertex_information);
6054 mesh_tasks += Threads::new_task(stringize_cell_to_vertex_information);
6055 mesh_tasks += Threads::new_task(stringize_cell_offset_and_type_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:
6059 const Table<2, float> data_vectors =
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
6075 data_tasks += Threads::new_task([&, range]() {
6076 return stringize_nonscalar_data_range(data_vectors, range);
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 {
6084 data_tasks += Threads::new_task([&, data_set]() {
6085 return stringize_scalar_data_set(data_vectors, data_set);
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);
6184 i <= std::get<1>(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);
6198 i < std::get<1>(nonscalar_data_range);
6199 ++i)
6200 out << data_names[i] << "__";
6201 out << data_names[std::get<1>(nonscalar_data_range)];
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>>>
6345 &times_and_piece_names)
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)
6412 margin_in_percent = 5;
6413
6414
6415 // determine the bounding box in the model space
6416 double x_dimension, y_dimension, z_dimension;
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
6425 Point<spacedim> projected_point;
6426 std::array<Point<spacedim>, 4> projected_points;
6427
6428 Point<2> projection_decomposition;
6429 std::array<Point<2>, 4> projection_decompositions;
6430
6431 projected_point =
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);
6464 projected_points[3] = get_equispaced_location(patch,
6465 {i1 + 1, i2 + 1},
6466 n_subdivisions);
6467
6468 x_min = std::min(x_min, projected_points[0][0]);
6469 x_min = std::min(x_min, projected_points[1][0]);
6470 x_min = std::min(x_min, projected_points[2][0]);
6471 x_min = std::min(x_min, projected_points[3][0]);
6472
6473 x_max = std::max(x_max, projected_points[0][0]);
6474 x_max = std::max(x_max, projected_points[1][0]);
6475 x_max = std::max(x_max, projected_points[2][0]);
6476 x_max = std::max(x_max, projected_points[3][0]);
6477
6478 y_min = std::min(y_min, projected_points[0][1]);
6479 y_min = std::min(y_min, projected_points[1][1]);
6480 y_min = std::min(y_min, projected_points[2][1]);
6481 y_min = std::min(y_min, projected_points[3][1]);
6482
6483 y_max = std::max(y_max, projected_points[0][1]);
6484 y_max = std::max(y_max, projected_points[1][1]);
6485 y_max = std::max(y_max, projected_points[2][1]);
6486 y_max = std::max(y_max, projected_points[3][1]);
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
6525 x_dimension = x_max - x_min;
6526 y_dimension = y_max - y_min;
6527 z_dimension = z_max - z_min;
6528
6529
6530 // set initial camera position
6531 Point<3> camera_position;
6532 Point<3> camera_direction;
6533 Point<3> camera_horizontal;
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
6549 camera_focus = .5 * z_dimension;
6550
6551 Point<3> camera_position_temp;
6552 Point<3> camera_direction_temp;
6553 Point<3> camera_horizontal_temp;
6554
6555 const float angle_factor = 3.14159265f / 180.f;
6556
6557 // (I) rotate the camera to the chosen polar angle
6558 camera_position_temp[1] =
6559 std::cos(angle_factor * flags.polar_angle) * camera_position[1] -
6560 std::sin(angle_factor * flags.polar_angle) * camera_position[2];
6561 camera_position_temp[2] =
6562 std::sin(angle_factor * flags.polar_angle) * camera_position[1] +
6563 std::cos(angle_factor * flags.polar_angle) * camera_position[2];
6564
6565 camera_direction_temp[1] =
6566 std::cos(angle_factor * flags.polar_angle) * camera_direction[1] -
6567 std::sin(angle_factor * flags.polar_angle) * camera_direction[2];
6568 camera_direction_temp[2] =
6569 std::sin(angle_factor * flags.polar_angle) * camera_direction[1] +
6570 std::cos(angle_factor * flags.polar_angle) * camera_direction[2];
6571
6572 camera_horizontal_temp[1] =
6573 std::cos(angle_factor * flags.polar_angle) * camera_horizontal[1] -
6574 std::sin(angle_factor * flags.polar_angle) * camera_horizontal[2];
6575 camera_horizontal_temp[2] =
6576 std::sin(angle_factor * flags.polar_angle) * camera_horizontal[1] +
6577 std::cos(angle_factor * flags.polar_angle) * camera_horizontal[2];
6578
6579 camera_position[1] = camera_position_temp[1];
6580 camera_position[2] = camera_position_temp[2];
6581
6582 camera_direction[1] = camera_direction_temp[1];
6583 camera_direction[2] = camera_direction_temp[2];
6584
6585 camera_horizontal[1] = camera_horizontal_temp[1];
6586 camera_horizontal[2] = camera_horizontal_temp[2];
6587
6588 // (II) rotate the camera to the chosen azimuth angle
6589 camera_position_temp[0] =
6590 std::cos(angle_factor * flags.azimuth_angle) * camera_position[0] -
6591 std::sin(angle_factor * flags.azimuth_angle) * camera_position[1];
6592 camera_position_temp[1] =
6593 std::sin(angle_factor * flags.azimuth_angle) * camera_position[0] +
6594 std::cos(angle_factor * flags.azimuth_angle) * camera_position[1];
6595
6596 camera_direction_temp[0] =
6597 std::cos(angle_factor * flags.azimuth_angle) * camera_direction[0] -
6598 std::sin(angle_factor * flags.azimuth_angle) * camera_direction[1];
6599 camera_direction_temp[1] =
6600 std::sin(angle_factor * flags.azimuth_angle) * camera_direction[0] +
6601 std::cos(angle_factor * flags.azimuth_angle) * camera_direction[1];
6602
6603 camera_horizontal_temp[0] =
6604 std::cos(angle_factor * flags.azimuth_angle) * camera_horizontal[0] -
6605 std::sin(angle_factor * flags.azimuth_angle) * camera_horizontal[1];
6606 camera_horizontal_temp[1] =
6607 std::sin(angle_factor * flags.azimuth_angle) * camera_horizontal[0] +
6608 std::cos(angle_factor * flags.azimuth_angle) * camera_horizontal[1];
6609
6610 camera_position[0] = camera_position_temp[0];
6611 camera_position[1] = camera_position_temp[1];
6612
6613 camera_direction[0] = camera_direction_temp[0];
6614 camera_direction[1] = camera_direction_temp[1];
6615
6616 camera_horizontal[0] = camera_horizontal_temp[0];
6617 camera_horizontal[1] = camera_horizontal_temp[1];
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) *
6624 std::sin(angle_factor * flags.polar_angle) *
6625 std::sin(angle_factor * flags.azimuth_angle);
6626 camera_position[1] -= (z_min + 2. * z_dimension) *
6627 std::sin(angle_factor * flags.polar_angle) *
6628 std::cos(angle_factor * flags.azimuth_angle);
6629
6630
6631 // determine the bounding box on the projection plane
6632 double x_min_perspective, y_min_perspective;
6633 double x_max_perspective, y_max_perspective;
6634 double x_dimension_perspective, y_dimension_perspective;
6635
6636 n_subdivisions = first_patch.n_subdivisions;
6637 n = n_subdivisions + 1;
6638
6639 Point<3> point;
6640
6641 projected_point =
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
6655 projection_decomposition = svg_project_point(point,
6656 camera_position,
6657 camera_direction,
6658 camera_horizontal,
6659 camera_focus);
6660
6661 x_min_perspective = projection_decomposition[0];
6662 x_max_perspective = projection_decomposition[0];
6663 y_min_perspective = projection_decomposition[1];
6664 y_max_perspective = projection_decomposition[1];
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),
6678 get_equispaced_location(patch,
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 = {
6689 {Point<3>{projected_vertices[0][0],
6690 projected_vertices[0][1],
6691 patch.data.n_rows() != 0 ?
6692 patch.data(0, i1 * d1 + i2 * d2) :
6693 0},
6694 Point<3>{projected_vertices[1][0],
6695 projected_vertices[1][1],
6696 patch.data.n_rows() != 0 ?
6697 patch.data(0, (i1 + 1) * d1 + i2 * d2) :
6698 0},
6699 Point<3>{projected_vertices[2][0],
6700 projected_vertices[2][1],
6701 patch.data.n_rows() != 0 ?
6702 patch.data(0, i1 * d1 + (i2 + 1) * d2) :
6703 0},
6704 Point<3>{projected_vertices[3][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
6710 projection_decompositions = {
6711 {svg_project_point(vertices[0],
6712 camera_position,
6713 camera_direction,
6714 camera_horizontal,
6715 camera_focus),
6716 svg_project_point(vertices[1],
6717 camera_position,
6718 camera_direction,
6719 camera_horizontal,
6720 camera_focus),
6721 svg_project_point(vertices[2],
6722 camera_position,
6723 camera_direction,
6724 camera_horizontal,
6725 camera_focus),
6726 svg_project_point(vertices[3],
6727 camera_position,
6728 camera_direction,
6729 camera_horizontal,
6730 camera_focus)}};
6731
6732 x_min_perspective =
6733 std::min(x_min_perspective,
6734 static_cast<double>(
6735 projection_decompositions[0][0]));
6736 x_min_perspective =
6737 std::min(x_min_perspective,
6738 static_cast<double>(
6739 projection_decompositions[1][0]));
6740 x_min_perspective =
6741 std::min(x_min_perspective,
6742 static_cast<double>(
6743 projection_decompositions[2][0]));
6744 x_min_perspective =
6745 std::min(x_min_perspective,
6746 static_cast<double>(
6747 projection_decompositions[3][0]));
6748
6749 x_max_perspective =
6750 std::max(x_max_perspective,
6751 static_cast<double>(
6752 projection_decompositions[0][0]));
6753 x_max_perspective =
6754 std::max(x_max_perspective,
6755 static_cast<double>(
6756 projection_decompositions[1][0]));
6757 x_max_perspective =
6758 std::max(x_max_perspective,
6759 static_cast<double>(
6760 projection_decompositions[2][0]));
6761 x_max_perspective =
6762 std::max(x_max_perspective,
6763 static_cast<double>(
6764 projection_decompositions[3][0]));
6765
6766 y_min_perspective =
6767 std::min(y_min_perspective,
6768 static_cast<double>(
6769 projection_decompositions[0][1]));
6770 y_min_perspective =
6771 std::min(y_min_perspective,
6772 static_cast<double>(
6773 projection_decompositions[1][1]));
6774 y_min_perspective =
6775 std::min(y_min_perspective,
6776 static_cast<double>(
6777 projection_decompositions[2][1]));
6778 y_min_perspective =
6779 std::min(y_min_perspective,
6780 static_cast<double>(
6781 projection_decompositions[3][1]));
6782
6783 y_max_perspective =
6784 std::max(y_max_perspective,
6785 static_cast<double>(
6786 projection_decompositions[0][1]));
6787 y_max_perspective =
6788 std::max(y_max_perspective,
6789 static_cast<double>(
6790 projection_decompositions[1][1]));
6791 y_max_perspective =
6792 std::max(y_max_perspective,
6793 static_cast<double>(
6794 projection_decompositions[2][1]));
6795 y_max_perspective =
6796 std::max(y_max_perspective,
6797 static_cast<double>(
6798 projection_decompositions[3][1]));
6799 }
6800 }
6801 }
6802
6803 x_dimension_perspective = x_max_perspective - x_min_perspective;
6804 y_dimension_perspective = y_max_perspective - y_min_perspective;
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),
6821 get_equispaced_location(patch,
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],
6860 camera_position,
6861 camera_direction,
6862 camera_horizontal,
6863 camera_focus);
6864 cell.projected_vertices[1] =
6865 svg_project_point(cell.vertices[1],
6866 camera_position,
6867 camera_direction,
6868 camera_horizontal,
6869 camera_focus);
6870 cell.projected_vertices[2] =
6871 svg_project_point(cell.vertices[2],
6872 camera_position,
6873 camera_direction,
6874 camera_horizontal,
6875 camera_focus);
6876 cell.projected_vertices[3] =
6877 svg_project_point(cell.vertices[3],
6878 camera_position,
6879 camera_direction,
6880 camera_horizontal,
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,
6886 camera_position,
6887 camera_direction,
6888 camera_horizontal,
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>(
6902 .5 + height * (x_dimension_perspective / y_dimension_perspective));
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 {
6922 Point<3> points3d_triangle[3];
6923
6924 for (unsigned int triangle_index = 0; triangle_index < 4;
6925 triangle_index++)
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
6953 Point<6> gradient_param =
6954 svg_get_gradient_parameters(points3d_triangle);
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
7038 Point<3> gradient_start_point_3d, gradient_stop_point_3d;
7039
7040 gradient_start_point_3d[0] = gradient_param[0];
7041 gradient_start_point_3d[1] = gradient_param[1];
7042 gradient_start_point_3d[2] = gradient_param[4];
7043
7044 gradient_stop_point_3d[0] = gradient_param[2];
7045 gradient_stop_point_3d[1] = gradient_param[3];
7046 gradient_stop_point_3d[2] = gradient_param[5];
7047
7048 Point<2> gradient_start_point =
7049 svg_project_point(gradient_start_point_3d,
7050 camera_position,
7051 camera_direction,
7052 camera_horizontal,
7053 camera_focus);
7054 Point<2> gradient_stop_point =
7055 svg_project_point(gradient_stop_point_3d,
7056 camera_position,
7057 camera_direction,
7058 camera_horizontal,
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 +
7067 ((gradient_start_point[0] - x_min_perspective) /
7068 x_dimension_perspective) *
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 -
7075 ((gradient_start_point[1] - y_min_perspective) /
7076 y_dimension_perspective) *
7077 (height - (height / 100.) * 2. * margin_in_percent))
7078 << "\" "
7079 << "x2=\""
7080 << static_cast<unsigned int>(
7081 .5 +
7082 ((gradient_stop_point[0] - x_min_perspective) /
7083 x_dimension_perspective) *
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 -
7090 ((gradient_stop_point[1] - y_min_perspective) /
7091 y_dimension_perspective) *
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 +
7139 ((x1 - x_min_perspective) / x_dimension_perspective) *
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 -
7145 ((y1 - y_min_perspective) / y_dimension_perspective) *
7146 (height - (height / 100.) * 2. * margin_in_percent))
7147 << " L "
7148 << static_cast<unsigned int>(
7149 .5 +
7150 ((x2 - x_min_perspective) / x_dimension_perspective) *
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 -
7156 ((y2 - y_min_perspective) / y_dimension_perspective) *
7157 (height - (height / 100.) * 2. * margin_in_percent))
7158 << " L "
7159 << static_cast<unsigned int>(
7160 .5 +
7161 ((x3 - x_min_perspective) / x_dimension_perspective) *
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 -
7167 ((y3 - y_min_perspective) / y_dimension_perspective) *
7168 (height - (height / 100.) * 2. * margin_in_percent))
7169 << " L "
7170 << static_cast<unsigned int>(
7171 .5 +
7172 ((x1 - x_min_perspective) / x_dimension_perspective) *
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 -
7178 ((y1 - y_min_perspective) / y_dimension_perspective) *
7179 (height - (height / 100.) * 2. * margin_in_percent))
7180 << "\" style=\"stroke:black; fill:url(#" << triangle_counter
7181 << "); stroke-width:" << flags.line_thickness << "\"/>" << '\n';
7182
7183 ++triangle_counter;
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)
7200 additional_width =
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=\""
7315 << element_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,
7420 const CompressionLevel compression)
7421 {
7422#ifndef DEAL_II_WITH_MPI
7423 (void)patches;
7424 (void)data_names;
7425 (void)nonscalar_data_ranges;
7426 (void)flags;
7427 (void)filename;
7428 (void)comm;
7429 (void)compression;
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
7458 if (compression != CompressionLevel::no_compression)
7459# ifdef DEAL_II_WITH_ZLIB
7460 f.push(boost::iostreams::zlib_compressor(
7461 get_boost_zlib_compression_level(compression)));
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
7473 write_deal_II_intermediate<dim, spacedim>(
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
7482 const ParallelIntermediateHeader header{
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,
7498 Utilities::MPI::mpi_type_id_for_type<std::uint64_t>,
7499 static_cast<std::uint64_t *>(chunk_sizes.data()),
7500 1,
7501 Utilities::MPI::mpi_type_id_for_type<std::uint64_t>,
7502 0,
7503 comm);
7504 AssertThrowMPI(ierr);
7505
7506 MPI_Info info;
7507 ierr = MPI_Info_create(&info);
7508 AssertThrowMPI(ierr);
7509 MPI_File fh;
7510 ierr = MPI_File_open(
7511 comm, filename.c_str(), MPI_MODE_CREATE | MPI_MODE_WRONLY, info, &fh);
7512 AssertThrow(ierr == MPI_SUCCESS, ExcFileNotOpen(filename));
7513 ierr = MPI_Info_free(&info);
7514 AssertThrowMPI(ierr);
7515
7516 // Delete the file contents:
7517 ierr = MPI_File_set_size(fh, 0);
7518 AssertThrowMPI(ierr);
7519 // This barrier is necessary, because otherwise others might already write
7520 // while one core is still setting the size to zero.
7521 ierr = MPI_Barrier(comm);
7522 AssertThrowMPI(ierr);
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);
7529 AssertThrowMPI(ierr);
7530
7532 fh,
7533 /* offset = */ sizeof(header),
7534 chunk_sizes.data(),
7535 chunk_sizes.size(),
7536 Utilities::MPI::mpi_type_id_for_type<std::uint64_t>,
7537 MPI_STATUS_IGNORE);
7538 AssertThrowMPI(ierr);
7539 }
7540
7541 // Write the main part on each rank:
7542 {
7543 std::uint64_t prefix_sum = 0;
7544 ierr = MPI_Exscan(&my_size,
7545 &prefix_sum,
7546 1,
7547 Utilities::MPI::mpi_type_id_for_type<std::uint64_t>,
7548 MPI_SUM,
7549 comm);
7550 AssertThrowMPI(ierr);
7551
7552 // Locate specific offset for each processor.
7553 const MPI_Offset offset = static_cast<MPI_Offset>(sizeof(header)) +
7554 n_ranks * sizeof(std::uint64_t) + prefix_sum;
7555
7557 fh, offset, my_buffer.data(), my_size, MPI_CHAR, MPI_STATUS_IGNORE);
7558 AssertThrowMPI(ierr);
7559 }
7560
7561 // Make sure we sync to disk. As written in the standard,
7562 // MPI_File_close() actually already implies a sync but there seems
7563 // to be a bug on at least one configuration (running with multiple
7564 // nodes using OpenMPI 4.1) that requires it. Without this call, the
7565 // footer is sometimes missing.
7566 ierr = MPI_File_sync(fh);
7567 AssertThrowMPI(ierr);
7568
7569 ierr = MPI_File_close(&fh);
7570 AssertThrowMPI(ierr);
7571#endif
7572 }
7573
7574
7575
7576 std::pair<unsigned int, unsigned int>
7578 {
7579 AssertThrow(input.fail() == false, ExcIO());
7580
7581 unsigned int dim, spacedim;
7582 input >> dim >> spacedim;
7583
7584 return std::make_pair(dim, spacedim);
7585 }
7586} // namespace DataOutBase
7587
7588
7589
7590/* --------------------------- class DataOutInterface ---------------------- */
7591
7592
7593template <int dim, int spacedim>
7595 : default_subdivisions(1)
7596 , default_fmt(DataOutBase::default_format)
7597{}
7598
7599
7600
7601template <int dim, int spacedim>
7602void
7604{
7605 DataOutBase::write_dx(get_patches(),
7606 get_dataset_names(),
7607 get_nonscalar_data_ranges(),
7608 dx_flags,
7609 out);
7610}
7611
7612
7613
7614template <int dim, int spacedim>
7615void
7617{
7618 DataOutBase::write_ucd(get_patches(),
7619 get_dataset_names(),
7620 get_nonscalar_data_ranges(),
7621 ucd_flags,
7622 out);
7623}
7624
7625
7626
7627template <int dim, int spacedim>
7628void
7630{
7631 DataOutBase::write_gnuplot(get_patches(),
7632 get_dataset_names(),
7633 get_nonscalar_data_ranges(),
7634 gnuplot_flags,
7635 out);
7636}
7637
7638
7639
7640template <int dim, int spacedim>
7641void
7643{
7644 DataOutBase::write_povray(get_patches(),
7645 get_dataset_names(),
7646 get_nonscalar_data_ranges(),
7647 povray_flags,
7648 out);
7649}
7650
7651
7652
7653template <int dim, int spacedim>
7654void
7656{
7657 DataOutBase::write_eps(get_patches(),
7658 get_dataset_names(),
7659 get_nonscalar_data_ranges(),
7660 eps_flags,
7661 out);
7662}
7663
7664
7665
7666template <int dim, int spacedim>
7667void
7669{
7670 DataOutBase::write_gmv(get_patches(),
7671 get_dataset_names(),
7672 get_nonscalar_data_ranges(),
7673 gmv_flags,
7674 out);
7675}
7676
7677
7678
7679template <int dim, int spacedim>
7680void
7682{
7683 DataOutBase::write_tecplot(get_patches(),
7684 get_dataset_names(),
7685 get_nonscalar_data_ranges(),
7686 tecplot_flags,
7687 out);
7688}
7689
7690
7691
7692template <int dim, int spacedim>
7693void
7695{
7696 DataOutBase::write_vtk(get_patches(),
7697 get_dataset_names(),
7698 get_nonscalar_data_ranges(),
7699 vtk_flags,
7700 out);
7701}
7702
7703template <int dim, int spacedim>
7704void
7706{
7707 DataOutBase::write_vtu(get_patches(),
7708 get_dataset_names(),
7709 get_nonscalar_data_ranges(),
7710 vtk_flags,
7711 out);
7712}
7713
7714template <int dim, int spacedim>
7715void
7717{
7718 DataOutBase::write_svg(get_patches(),
7719 get_dataset_names(),
7720 get_nonscalar_data_ranges(),
7721 svg_flags,
7722 out);
7723}
7724
7725
7726template <int dim, int spacedim>
7727void
7729 const std::string &filename,
7730 const MPI_Comm comm) const
7731{
7732#ifndef DEAL_II_WITH_MPI
7733 // without MPI fall back to the normal way to write a vtu file :
7734 (void)comm;
7735
7736 std::ofstream f(filename);
7737 AssertThrow(f, ExcFileNotOpen(filename));
7738 write_vtu(f);
7739#else
7740
7741 const unsigned int myrank = Utilities::MPI::this_mpi_process(comm);
7742 const unsigned int n_ranks = Utilities::MPI::n_mpi_processes(comm);
7743 MPI_Info info;
7744 int ierr = MPI_Info_create(&info);
7745 AssertThrowMPI(ierr);
7746 MPI_File fh;
7747 ierr = MPI_File_open(
7748 comm, filename.c_str(), MPI_MODE_CREATE | MPI_MODE_WRONLY, info, &fh);
7749 AssertThrow(ierr == MPI_SUCCESS, ExcFileNotOpen(filename));
7750
7751 ierr = MPI_File_set_size(fh, 0); // delete the file contents
7752 AssertThrowMPI(ierr);
7753 // this barrier is necessary, because otherwise others might already write
7754 // while one core is still setting the size to zero.
7755 ierr = MPI_Barrier(comm);
7756 AssertThrowMPI(ierr);
7757 ierr = MPI_Info_free(&info);
7758 AssertThrowMPI(ierr);
7759
7760 // Define header size so we can broadcast later.
7761 unsigned int header_size;
7762 std::uint64_t footer_offset;
7763
7764 // write header
7765 if (myrank == 0)
7766 {
7767 std::stringstream ss;
7768 DataOutBase::write_vtu_header(ss, vtk_flags);
7769 header_size = ss.str().size();
7770 // Write the header on rank 0 at the start of a file, i.e., offset 0.
7772 fh, 0, ss.str().c_str(), header_size, MPI_CHAR, MPI_STATUS_IGNORE);
7773 AssertThrowMPI(ierr);
7774 }
7775
7776 ierr = MPI_Bcast(&header_size, 1, MPI_UNSIGNED, 0, comm);
7777 AssertThrowMPI(ierr);
7778
7779 {
7780 const auto &patches = get_patches();
7781 const types::global_dof_index my_n_patches = patches.size();
7782 const types::global_dof_index global_n_patches =
7783 Utilities::MPI::sum(my_n_patches, comm);
7784
7785 // Do not write pieces with 0 cells as this will crash paraview if this is
7786 // the first piece written. But if nobody has any pieces to write (file is
7787 // empty), let processor 0 write their empty data, otherwise the vtk file is
7788 // invalid.
7789 std::stringstream ss;
7790 if (my_n_patches > 0 || (global_n_patches == 0 && myrank == 0))
7792 get_dataset_names(),
7793 get_nonscalar_data_ranges(),
7794 vtk_flags,
7795 ss);
7796
7797 // Use prefix sum to find specific offset to write at.
7798 const std::uint64_t size_on_proc = ss.str().size();
7799 std::uint64_t prefix_sum = 0;
7800 ierr = MPI_Exscan(&size_on_proc,
7801 &prefix_sum,
7802 1,
7803 Utilities::MPI::mpi_type_id_for_type<std::uint64_t>,
7804 MPI_SUM,
7805 comm);
7806 AssertThrowMPI(ierr);
7807
7808 // Locate specific offset for each processor.
7809 const MPI_Offset offset = static_cast<MPI_Offset>(header_size) + prefix_sum;
7810
7812 offset,
7813 ss.str().c_str(),
7814 ss.str().size(),
7815 MPI_CHAR,
7816 MPI_STATUS_IGNORE);
7817 AssertThrowMPI(ierr);
7818
7819 if (myrank == n_ranks - 1)
7820 {
7821 // Locating Footer with offset on last rank.
7822 footer_offset = size_on_proc + offset;
7823
7824 std::stringstream ss;
7826 const unsigned int footer_size = ss.str().size();
7827
7828 // Writing footer:
7830 footer_offset,
7831 ss.str().c_str(),
7832 footer_size,
7833 MPI_CHAR,
7834 MPI_STATUS_IGNORE);
7835 AssertThrowMPI(ierr);
7836 }
7837 }
7838
7839 // Make sure we sync to disk. As written in the standard,
7840 // MPI_File_close() actually already implies a sync but there seems
7841 // to be a bug on at least one configuration (running with multiple
7842 // nodes using OpenMPI 4.1) that requires it. Without this call, the
7843 // footer is sometimes missing.
7844 ierr = MPI_File_sync(fh);
7845 AssertThrowMPI(ierr);
7846
7847 ierr = MPI_File_close(&fh);
7848 AssertThrowMPI(ierr);
7849#endif
7850}
7851
7852
7853
7854template <int dim, int spacedim>
7855void
7857 std::ostream &out,
7858 const std::vector<std::string> &piece_names) const
7859{
7861 piece_names,
7862 get_dataset_names(),
7863 get_nonscalar_data_ranges(),
7864 vtk_flags);
7865}
7866
7867
7868
7869template <int dim, int spacedim>
7870std::string
7872 const std::string &directory,
7873 const std::string &filename_without_extension,
7874 const unsigned int counter,
7875 const MPI_Comm mpi_communicator,
7876 const unsigned int n_digits_for_counter,
7877 const unsigned int n_groups) const
7878{
7879 const unsigned int rank = Utilities::MPI::this_mpi_process(mpi_communicator);
7880 const unsigned int n_ranks =
7881 Utilities::MPI::n_mpi_processes(mpi_communicator);
7882 const unsigned int n_files_written =
7883 (n_groups == 0 || n_groups > n_ranks) ? n_ranks : n_groups;
7884
7885 Assert(n_files_written >= 1, ExcInternalError());
7886 // the "-1" is needed since we use C++ style counting starting with 0, so
7887 // writing 10 files means the filename runs from 0 to 9
7888 const unsigned int n_digits =
7889 Utilities::needed_digits(std::max(0, int(n_files_written) - 1));
7890
7891 const unsigned int color = rank % n_files_written;
7892 const std::string filename =
7893 directory + filename_without_extension + "_" +
7894 Utilities::int_to_string(counter, n_digits_for_counter) + "." +
7895 Utilities::int_to_string(color, n_digits) + ".vtu";
7896
7897 if (n_groups == 0 || n_groups > n_ranks)
7898 {
7899 // every processor writes one file
7900 std::ofstream output(filename);
7901 AssertThrow(output, ExcFileNotOpen(filename));
7902 this->write_vtu(output);
7903 }
7904 else if (n_groups == 1)
7905 {
7906 // write only a single data file in parallel
7907 this->write_vtu_in_parallel(filename, mpi_communicator);
7908 }
7909 else
7910 {
7911#ifdef DEAL_II_WITH_MPI
7912 // write n_groups data files
7913 MPI_Comm comm_group;
7914 int ierr = MPI_Comm_split(mpi_communicator, color, rank, &comm_group);
7915 AssertThrowMPI(ierr);
7916 this->write_vtu_in_parallel(filename, comm_group);
7918#else
7919 AssertThrow(false, ExcMessage("Logical error. Should not arrive here."));
7920#endif
7921 }
7922
7923 // write pvtu record
7924 const std::string pvtu_filename =
7925 filename_without_extension + "_" +
7926 Utilities::int_to_string(counter, n_digits_for_counter) + ".pvtu";
7927
7928 if (rank == 0)
7929 {
7930 std::vector<std::string> filename_vector;
7931 for (unsigned int i = 0; i < n_files_written; ++i)
7932 {
7933 const std::string filename =
7934 filename_without_extension + "_" +
7935 Utilities::int_to_string(counter, n_digits_for_counter) + "." +
7936 Utilities::int_to_string(i, n_digits) + ".vtu";
7937
7938 filename_vector.emplace_back(filename);
7939 }
7940
7941 std::ofstream pvtu_output(directory + pvtu_filename);
7942 this->write_pvtu_record(pvtu_output, filename_vector);
7943 }
7944
7945 return pvtu_filename;
7946}
7947
7948
7949
7950template <int dim, int spacedim>
7951void
7953 std::ostream &out) const
7954{
7956 get_dataset_names(),
7957 get_nonscalar_data_ranges(),
7958 deal_II_intermediate_flags,
7959 out);
7960}
7961
7962
7963
7964template <int dim, int spacedim>
7965void
7967 const std::string &filename,
7968 const MPI_Comm comm,
7969 const DataOutBase::CompressionLevel compression) const
7970{
7972 get_patches(),
7973 get_dataset_names(),
7974 get_nonscalar_data_ranges(),
7975 deal_II_intermediate_flags,
7976 filename,
7977 comm,
7978 compression);
7979}
7980
7981
7982
7983template <int dim, int spacedim>
7986 const DataOutBase::DataOutFilter &data_filter,
7987 const std::string &h5_filename,
7988 const double cur_time,
7989 const MPI_Comm comm) const
7990{
7991 return create_xdmf_entry(
7992 data_filter, h5_filename, h5_filename, cur_time, comm);
7993}
7994
7995
7996
7997template <int dim, int spacedim>
8000 const DataOutBase::DataOutFilter &data_filter,
8001 const std::string &h5_mesh_filename,
8002 const std::string &h5_solution_filename,
8003 const double cur_time,
8004 const MPI_Comm comm) const
8005{
8006 AssertThrow(spacedim == 2 || spacedim == 3,
8007 ExcMessage("XDMF only supports 2 or 3 space dimensions."));
8008
8009#ifndef DEAL_II_WITH_HDF5
8010 // throw an exception, but first make sure the compiler does not warn about
8011 // the now unused function arguments
8012 (void)data_filter;
8013 (void)h5_mesh_filename;
8014 (void)h5_solution_filename;
8015 (void)cur_time;
8016 (void)comm;
8017 AssertThrow(false, ExcMessage("XDMF support requires HDF5 to be turned on."));
8018
8019 return {};
8020
8021#else
8022
8023 std::uint64_t local_node_cell_count[2], global_node_cell_count[2];
8024
8025 local_node_cell_count[0] = data_filter.n_nodes();
8026 local_node_cell_count[1] = data_filter.n_cells();
8027
8028 const int myrank = Utilities::MPI::this_mpi_process(comm);
8029 // And compute the global total
8030 int ierr = MPI_Allreduce(local_node_cell_count,
8031 global_node_cell_count,
8032 2,
8033 Utilities::MPI::mpi_type_id_for_type<std::uint64_t>,
8034 MPI_SUM,
8035 comm);
8036 AssertThrowMPI(ierr);
8037
8038 // The implementation is a bit complicated because we are supposed to return
8039 // the correct data on rank 0 and an empty object on all other ranks but all
8040 // information (for example the attributes) are only available on ranks that
8041 // have any cells.
8042 // We will identify the smallest rank that has data and then communicate
8043 // from this rank to rank 0 (if they are different ranks).
8044
8045 const bool have_data = (data_filter.n_nodes() > 0);
8046 MPI_Comm split_comm;
8047 {
8048 const int key = myrank;
8049 const int color = (have_data ? 1 : 0);
8050 const int ierr = MPI_Comm_split(comm, color, key, &split_comm);
8051 AssertThrowMPI(ierr);
8052 }
8053
8054 const bool am_i_first_rank_with_data =
8055 have_data && (Utilities::MPI::this_mpi_process(split_comm) == 0);
8056
8057 ierr = MPI_Comm_free(&split_comm);
8058 AssertThrowMPI(ierr);
8059
8060 const int tag = 47381;
8061
8062 // Output the XDMF file only on the root process of all ranks with data:
8063 if (am_i_first_rank_with_data)
8064 {
8065 const auto &patches = get_patches();
8066 Assert(patches.size() > 0, DataOutBase::ExcNoPatches());
8067
8068 // We currently don't support writing mixed meshes:
8069 if constexpr (running_in_debug_mode())
8070 {
8071 for (const auto &patch : patches)
8072 Assert(patch.reference_cell == patches[0].reference_cell,
8074 }
8075
8076 XDMFEntry entry(h5_mesh_filename,
8077 h5_solution_filename,
8078 cur_time,
8079 global_node_cell_count[0],
8080 global_node_cell_count[1],
8081 dim,
8082 spacedim,
8083 patches[0].reference_cell);
8084 const unsigned int n_data_sets = data_filter.n_data_sets();
8085
8086 // The vector names generated here must match those generated in
8087 // the HDF5 file
8088 for (unsigned int i = 0; i < n_data_sets; ++i)
8089 {
8090 entry.add_attribute(data_filter.get_data_set_name(i),
8091 data_filter.get_data_set_dim(i));
8092 }
8093
8094 if (myrank != 0)
8095 {
8096 // send to rank 0
8097 const std::vector<char> buffer = Utilities::pack(entry, false);
8098 ierr = MPI_Send(buffer.data(), buffer.size(), MPI_BYTE, 0, tag, comm);
8099 AssertThrowMPI(ierr);
8100
8101 return {};
8102 }
8103
8104 return entry;
8105 }
8106
8107 if (myrank == 0 && !am_i_first_rank_with_data)
8108 {
8109 // receive the XDMF data on rank 0 if we don't have it...
8110
8111 MPI_Status status;
8112 int ierr = MPI_Probe(MPI_ANY_SOURCE, tag, comm, &status);
8113 AssertThrowMPI(ierr);
8114
8115 int len;
8116 ierr = MPI_Get_count(&status, MPI_BYTE, &len);
8117 AssertThrowMPI(ierr);
8118
8119 std::vector<char> buffer(len);
8120 ierr = MPI_Recv(buffer.data(),
8121 len,
8122 MPI_BYTE,
8123 status.MPI_SOURCE,
8124 tag,
8125 comm,
8126 MPI_STATUS_IGNORE);
8127 AssertThrowMPI(ierr);
8128
8129 return Utilities::unpack<XDMFEntry>(buffer, false);
8130 }
8131
8132 // default case for any other rank is to return an empty object
8133 return {};
8134#endif
8135}
8136
8137template <int dim, int spacedim>
8138void
8140 const std::vector<XDMFEntry> &entries,
8141 const std::string &filename,
8142 const MPI_Comm comm) const
8143{
8144#ifdef DEAL_II_WITH_MPI
8145 const int myrank = Utilities::MPI::this_mpi_process(comm);
8146#else
8147 (void)comm;
8148 const int myrank = 0;
8149#endif
8150
8151 // Only rank 0 process writes the XDMF file
8152 if (myrank == 0)
8153 {
8154 std::ofstream xdmf_file(filename);
8155
8156 xdmf_file << "<?xml version=\"1.0\" ?>\n";
8157 xdmf_file << "<!DOCTYPE Xdmf SYSTEM \"Xdmf.dtd\" []>\n";
8158 xdmf_file << "<Xdmf Version=\"2.0\">\n";
8159 xdmf_file << " <Domain>\n";
8160 xdmf_file
8161 << " <Grid Name=\"CellTime\" GridType=\"Collection\" CollectionType=\"Temporal\">\n";
8162
8163 for (const auto &entry : entries)
8164 {
8165 xdmf_file << entry.get_xdmf_content(3);
8166 }
8167
8168 xdmf_file << " </Grid>\n";
8169 xdmf_file << " </Domain>\n";
8170 xdmf_file << "</Xdmf>\n";
8171
8172 xdmf_file.close();
8173 }
8174}
8175
8176
8177
8178/*
8179 * Write the data in this DataOutInterface to a DataOutFilter object. Filtering
8180 * is performed based on the DataOutFilter flags.
8181 */
8182template <int dim, int spacedim>
8183void
8185 DataOutBase::DataOutFilter &filtered_data) const
8186{
8188 get_dataset_names(),
8189 get_nonscalar_data_ranges(),
8190 filtered_data);
8191}
8192
8193
8194namespace
8195{
8196#ifdef DEAL_II_WITH_HDF5
8200 template <int dim, int spacedim>
8201 void
8202 do_write_hdf5(const std::vector<DataOutBase::Patch<dim, spacedim>> &patches,
8203 const DataOutBase::DataOutFilter &data_filter,
8204 const DataOutBase::Hdf5Flags &flags,
8205 const bool write_mesh_file,
8206 const std::string &mesh_filename,
8207 const std::string &solution_filename,
8208 const MPI_Comm comm)
8209 {
8210 hid_t h5_mesh_file_id = -1, h5_solution_file_id, file_plist_id, plist_id;
8211 hid_t node_dataspace, node_dataset, node_file_dataspace,
8212 node_memory_dataspace, node_dataset_id;
8213 hid_t cell_dataspace, cell_dataset, cell_file_dataspace,
8214 cell_memory_dataspace;
8215 hid_t pt_data_dataspace, pt_data_dataset, pt_data_file_dataspace,
8216 pt_data_memory_dataspace;
8217 herr_t status;
8218 std::uint64_t local_node_cell_count[2];
8219 hsize_t count[2], offset[2], node_ds_dim[2], cell_ds_dim[2];
8220 std::vector<double> node_data_vec;
8221 std::vector<unsigned int> cell_data_vec;
8222
8223
8224
8225 local_node_cell_count[0] = data_filter.n_nodes();
8226 local_node_cell_count[1] = data_filter.n_cells();
8227
8228 // Create file access properties
8229 file_plist_id = H5Pcreate(H5P_FILE_ACCESS);
8230 AssertThrow(file_plist_id != -1, ExcIO());
8231 // If MPI is enabled *and* HDF5 is parallel, we can do parallel output
8232# ifdef DEAL_II_WITH_MPI
8233# ifdef H5_HAVE_PARALLEL
8234 // Set the access to use the specified MPI_Comm object
8235 status = H5Pset_fapl_mpio(file_plist_id, comm, MPI_INFO_NULL);
8236 AssertThrow(status >= 0, ExcIO());
8237# endif
8238# endif
8239 // if zlib support is disabled flags are unused
8240# ifndef DEAL_II_WITH_ZLIB
8241 (void)flags;
8242# endif
8243
8244 // Compute the global total number of nodes/cells and determine the offset
8245 // of the data for this process
8246
8247 std::uint64_t global_node_cell_count[2] = {0, 0};
8248 std::uint64_t global_node_cell_offsets[2] = {0, 0};
8249
8250# ifdef DEAL_II_WITH_MPI
8251 int ierr =
8252 MPI_Allreduce(local_node_cell_count,
8253 global_node_cell_count,
8254 2,
8255 Utilities::MPI::mpi_type_id_for_type<std::uint64_t>,
8256 MPI_SUM,
8257 comm);
8258 AssertThrowMPI(ierr);
8259 ierr = MPI_Exscan(local_node_cell_count,
8260 global_node_cell_offsets,
8261 2,
8262 Utilities::MPI::mpi_type_id_for_type<std::uint64_t>,
8263 MPI_SUM,
8264 comm);
8265 AssertThrowMPI(ierr);
8266# else
8267 global_node_cell_count[0] = local_node_cell_count[0];
8268 global_node_cell_count[1] = local_node_cell_count[1];
8269 global_node_cell_offsets[0] = global_node_cell_offsets[1] = 0;
8270# endif
8271
8272 // Create the property list for a collective write
8273 plist_id = H5Pcreate(H5P_DATASET_XFER);
8274 AssertThrow(plist_id >= 0, ExcIO());
8275# ifdef DEAL_II_WITH_MPI
8276# ifdef H5_HAVE_PARALLEL
8277 status = H5Pset_dxpl_mpio(plist_id, H5FD_MPIO_COLLECTIVE);
8278 AssertThrow(status >= 0, ExcIO());
8279# endif
8280# endif
8281
8282 if (write_mesh_file)
8283 {
8284 // Overwrite any existing files (change this to an option?)
8285 h5_mesh_file_id = H5Fcreate(mesh_filename.c_str(),
8286 H5F_ACC_TRUNC,
8287 H5P_DEFAULT,
8288 file_plist_id);
8289 AssertThrow(h5_mesh_file_id >= 0, ExcIO());
8290
8291 // Create the dataspace for the nodes and cells. HDF5 only supports 2-
8292 // or 3-dimensional coordinates
8293 node_ds_dim[0] = global_node_cell_count[0];
8294 node_ds_dim[1] = (spacedim < 2) ? 2 : spacedim;
8295 node_dataspace = H5Screate_simple(2, node_ds_dim, nullptr);
8296 AssertThrow(node_dataspace >= 0, ExcIO());
8297
8298 cell_ds_dim[0] = global_node_cell_count[1];
8299 cell_ds_dim[1] = patches[0].reference_cell.n_vertices();
8300 cell_dataspace = H5Screate_simple(2, cell_ds_dim, nullptr);
8301 AssertThrow(cell_dataspace >= 0, ExcIO());
8302
8303 // Create the dataset for the nodes and cells
8304# if H5Gcreate_vers == 1
8305 node_dataset = H5Dcreate(h5_mesh_file_id,
8306 "nodes",
8307 H5T_NATIVE_DOUBLE,
8308 node_dataspace,
8309 H5P_DEFAULT);
8310# else
8311 node_dataset_id = H5Pcreate(H5P_DATASET_CREATE);
8312# ifdef DEAL_II_WITH_ZLIB
8313 H5Pset_deflate(node_dataset_id,
8314 get_zlib_compression_level(flags.compression_level));
8315 H5Pset_chunk(node_dataset_id, 2, node_ds_dim);
8316# endif
8317 node_dataset = H5Dcreate(h5_mesh_file_id,
8318 "nodes",
8319 H5T_NATIVE_DOUBLE,
8320 node_dataspace,
8321 H5P_DEFAULT,
8322 node_dataset_id,
8323 H5P_DEFAULT);
8324 H5Pclose(node_dataset_id);
8325# endif
8326 AssertThrow(node_dataset >= 0, ExcIO());
8327# if H5Gcreate_vers == 1
8328 cell_dataset = H5Dcreate(h5_mesh_file_id,
8329 "cells",
8330 H5T_NATIVE_UINT,
8331 cell_dataspace,
8332 H5P_DEFAULT);
8333# else
8334 node_dataset_id = H5Pcreate(H5P_DATASET_CREATE);
8335# ifdef DEAL_II_WITH_ZLIB
8336 H5Pset_deflate(node_dataset_id,
8337 get_zlib_compression_level(flags.compression_level));
8338 H5Pset_chunk(node_dataset_id, 2, cell_ds_dim);
8339# endif
8340 cell_dataset = H5Dcreate(h5_mesh_file_id,
8341 "cells",
8342 H5T_NATIVE_UINT,
8343 cell_dataspace,
8344 H5P_DEFAULT,
8345 node_dataset_id,
8346 H5P_DEFAULT);
8347 H5Pclose(node_dataset_id);
8348# endif
8349 AssertThrow(cell_dataset >= 0, ExcIO());
8350
8351 // Close the node and cell dataspaces since we're done with them
8352 status = H5Sclose(node_dataspace);
8353 AssertThrow(status >= 0, ExcIO());
8354 status = H5Sclose(cell_dataspace);
8355 AssertThrow(status >= 0, ExcIO());
8356
8357 // Create the data subset we'll use to read from memory. HDF5 only
8358 // supports 2- or 3-dimensional coordinates
8359 count[0] = local_node_cell_count[0];
8360 count[1] = (spacedim < 2) ? 2 : spacedim;
8361
8362 offset[0] = global_node_cell_offsets[0];
8363 offset[1] = 0;
8364
8365 node_memory_dataspace = H5Screate_simple(2, count, nullptr);
8366 AssertThrow(node_memory_dataspace >= 0, ExcIO());
8367
8368 // Select the hyperslab in the file
8369 node_file_dataspace = H5Dget_space(node_dataset);
8370 AssertThrow(node_file_dataspace >= 0, ExcIO());
8371 status = H5Sselect_hyperslab(
8372 node_file_dataspace, H5S_SELECT_SET, offset, nullptr, count, nullptr);
8373 AssertThrow(status >= 0, ExcIO());
8374
8375 // And repeat for cells
8376 count[0] = local_node_cell_count[1];
8377 count[1] = patches[0].reference_cell.n_vertices();
8378 offset[0] = global_node_cell_offsets[1];
8379 offset[1] = 0;
8380 cell_memory_dataspace = H5Screate_simple(2, count, nullptr);
8381 AssertThrow(cell_memory_dataspace >= 0, ExcIO());
8382
8383 cell_file_dataspace = H5Dget_space(cell_dataset);
8384 AssertThrow(cell_file_dataspace >= 0, ExcIO());
8385 status = H5Sselect_hyperslab(
8386 cell_file_dataspace, H5S_SELECT_SET, offset, nullptr, count, nullptr);
8387 AssertThrow(status >= 0, ExcIO());
8388
8389 // And finally, write the node data
8390 data_filter.fill_node_data(node_data_vec);
8391 status = H5Dwrite(node_dataset,
8392 H5T_NATIVE_DOUBLE,
8393 node_memory_dataspace,
8394 node_file_dataspace,
8395 plist_id,
8396 node_data_vec.data());
8397 AssertThrow(status >= 0, ExcIO());
8398 node_data_vec.clear();
8399
8400 // And the cell data
8401 data_filter.fill_cell_data(global_node_cell_offsets[0], cell_data_vec);
8402 status = H5Dwrite(cell_dataset,
8403 H5T_NATIVE_UINT,
8404 cell_memory_dataspace,
8405 cell_file_dataspace,
8406 plist_id,
8407 cell_data_vec.data());
8408 AssertThrow(status >= 0, ExcIO());
8409 cell_data_vec.clear();
8410
8411 // Close the file dataspaces
8412 status = H5Sclose(node_file_dataspace);
8413 AssertThrow(status >= 0, ExcIO());
8414 status = H5Sclose(cell_file_dataspace);
8415 AssertThrow(status >= 0, ExcIO());
8416
8417 // Close the memory dataspaces
8418 status = H5Sclose(node_memory_dataspace);
8419 AssertThrow(status >= 0, ExcIO());
8420 status = H5Sclose(cell_memory_dataspace);
8421 AssertThrow(status >= 0, ExcIO());
8422
8423 // Close the datasets
8424 status = H5Dclose(node_dataset);
8425 AssertThrow(status >= 0, ExcIO());
8426 status = H5Dclose(cell_dataset);
8427 AssertThrow(status >= 0, ExcIO());
8428
8429 // If the filenames are different, we need to close the mesh file
8430 if (mesh_filename != solution_filename)
8431 {
8432 status = H5Fclose(h5_mesh_file_id);
8433 AssertThrow(status >= 0, ExcIO());
8434 }
8435 }
8436
8437 // If the filenames are identical, continue with the same file
8438 if (mesh_filename == solution_filename && write_mesh_file)
8439 {
8440 h5_solution_file_id = h5_mesh_file_id;
8441 }
8442 else
8443 {
8444 // Otherwise we need to open a new file
8445 h5_solution_file_id = H5Fcreate(solution_filename.c_str(),
8446 H5F_ACC_TRUNC,
8447 H5P_DEFAULT,
8448 file_plist_id);
8449 AssertThrow(h5_solution_file_id >= 0, ExcIO());
8450 }
8451
8452 // when writing, first write out all vector data, then handle the scalar
8453 // data sets that have been left over
8454 unsigned int i;
8455 std::string vector_name;
8456 for (i = 0; i < data_filter.n_data_sets(); ++i)
8457 {
8458 // Allocate space for the point data
8459 // Must be either 1d or 3d
8460 const unsigned int pt_data_vector_dim = data_filter.get_data_set_dim(i);
8461 vector_name = data_filter.get_data_set_name(i);
8462
8463 // Create the dataspace for the point data
8464 node_ds_dim[0] = global_node_cell_count[0];
8465 node_ds_dim[1] = pt_data_vector_dim;
8466 pt_data_dataspace = H5Screate_simple(2, node_ds_dim, nullptr);
8467 AssertThrow(pt_data_dataspace >= 0, ExcIO());
8468
8469# if H5Gcreate_vers == 1
8470 pt_data_dataset = H5Dcreate(h5_solution_file_id,
8471 vector_name.c_str(),
8472 H5T_NATIVE_DOUBLE,
8473 pt_data_dataspace,
8474 H5P_DEFAULT);
8475# else
8476 node_dataset_id = H5Pcreate(H5P_DATASET_CREATE);
8477# ifdef DEAL_II_WITH_ZLIB
8478 H5Pset_deflate(node_dataset_id,
8479 get_zlib_compression_level(flags.compression_level));
8480 H5Pset_chunk(node_dataset_id, 2, node_ds_dim);
8481# endif
8482 pt_data_dataset = H5Dcreate(h5_solution_file_id,
8483 vector_name.c_str(),
8484 H5T_NATIVE_DOUBLE,
8485 pt_data_dataspace,
8486 H5P_DEFAULT,
8487 node_dataset_id,
8488 H5P_DEFAULT);
8489 H5Pclose(node_dataset_id);
8490# endif
8491 AssertThrow(pt_data_dataset >= 0, ExcIO());
8492
8493 // Create the data subset we'll use to read from memory
8494 count[0] = local_node_cell_count[0];
8495 count[1] = pt_data_vector_dim;
8496 offset[0] = global_node_cell_offsets[0];
8497 offset[1] = 0;
8498 pt_data_memory_dataspace = H5Screate_simple(2, count, nullptr);
8499 AssertThrow(pt_data_memory_dataspace >= 0, ExcIO());
8500
8501 // Select the hyperslab in the file
8502 pt_data_file_dataspace = H5Dget_space(pt_data_dataset);
8503 AssertThrow(pt_data_file_dataspace >= 0, ExcIO());
8504 status = H5Sselect_hyperslab(pt_data_file_dataspace,
8505 H5S_SELECT_SET,
8506 offset,
8507 nullptr,
8508 count,
8509 nullptr);
8510 AssertThrow(status >= 0, ExcIO());
8511
8512 // And finally, write the data
8513 status = H5Dwrite(pt_data_dataset,
8514 H5T_NATIVE_DOUBLE,
8515 pt_data_memory_dataspace,
8516 pt_data_file_dataspace,
8517 plist_id,
8518 data_filter.get_data_set(i));
8519 AssertThrow(status >= 0, ExcIO());
8520
8521 // Close the dataspaces
8522 status = H5Sclose(pt_data_dataspace);
8523 AssertThrow(status >= 0, ExcIO());
8524 status = H5Sclose(pt_data_memory_dataspace);
8525 AssertThrow(status >= 0, ExcIO());
8526 status = H5Sclose(pt_data_file_dataspace);
8527 AssertThrow(status >= 0, ExcIO());
8528 // Close the dataset
8529 status = H5Dclose(pt_data_dataset);
8530 AssertThrow(status >= 0, ExcIO());
8531 }
8532
8533 // Close the file property list
8534 status = H5Pclose(file_plist_id);
8535 AssertThrow(status >= 0, ExcIO());
8536
8537 // Close the parallel access
8538 status = H5Pclose(plist_id);
8539 AssertThrow(status >= 0, ExcIO());
8540
8541 // Close the file
8542 status = H5Fclose(h5_solution_file_id);
8543 AssertThrow(status >= 0, ExcIO());
8544 }
8545#endif
8546} // namespace
8547
8548
8549
8550template <int dim, int spacedim>
8551void
8553 const std::vector<Patch<dim, spacedim>> &patches,
8554 const std::vector<std::string> &data_names,
8555 const std::vector<
8556 std::tuple<unsigned int,
8557 unsigned int,
8558 std::string,
8560 &nonscalar_data_ranges,
8561 DataOutBase::DataOutFilter &filtered_data)
8562{
8563 const unsigned int n_data_sets = data_names.size();
8564
8565#ifndef DEAL_II_WITH_MPI
8566 // verify that there are indeed patches to be written out. most of the times,
8567 // people just forget to call build_patches when there are no patches, so a
8568 // warning is in order. that said, the assertion is disabled if we support MPI
8569 // since then it can happen that on the coarsest mesh, a processor simply has
8570 // no cells it actually owns, and in that case it is legit if there are no
8571 // patches
8572 Assert(patches.size() > 0, ExcNoPatches());
8573#else
8574 if (patches.empty())
8575 return;
8576#endif
8577
8578 unsigned int n_nodes;
8579 std::tie(n_nodes, std::ignore) = count_nodes_and_cells(patches);
8580
8581 // For the format we write here, we need to write all node values relating
8582 // to one variable at a time. We could in principle do this by looping
8583 // over all patches and extracting the values corresponding to the one
8584 // variable we're dealing with right now, and then start the process over
8585 // for the next variable with another loop over all patches.
8586 //
8587 // An easier way is to create a global table that for each variable
8588 // lists all values. This copying of data vectors can be done in the
8589 // background while we're already working on vertices and cells,
8590 // so do this on a separate task and when wanting to write out the
8591 // data, we wait for that task to finish.
8593 create_global_data_table_task = Threads::new_task(
8594 [&patches]() { return create_global_data_table(patches); });
8595
8596 // Write the nodes/cells to the DataOutFilter object.
8597 write_nodes(patches, filtered_data);
8598 write_cells(patches, filtered_data);
8599
8600 // Wait for the reordering to be done and retrieve the reordered data:
8601 const Table<2, double> data_vectors =
8602 std::move(*create_global_data_table_task.return_value());
8603
8604 // when writing, first write out all vector data, then handle the scalar data
8605 // sets that have been left over
8606 unsigned int i, n_th_vector, data_set, pt_data_vector_dim;
8607 std::string vector_name;
8608 for (n_th_vector = 0, data_set = 0; data_set < n_data_sets;)
8609 {
8610 // Advance n_th_vector to at least the current data set we are on
8611 while (n_th_vector < nonscalar_data_ranges.size() &&
8612 std::get<0>(nonscalar_data_ranges[n_th_vector]) < data_set)
8613 ++n_th_vector;
8614
8615 // Determine the dimension of this data
8616 if (n_th_vector < nonscalar_data_ranges.size() &&
8617 std::get<0>(nonscalar_data_ranges[n_th_vector]) == data_set)
8618 {
8619 // Multiple dimensions
8620 pt_data_vector_dim = std::get<1>(nonscalar_data_ranges[n_th_vector]) -
8621 std::get<0>(nonscalar_data_ranges[n_th_vector]) +
8622 1;
8623
8624 // Ensure the dimensionality of the data is correct
8626 std::get<1>(nonscalar_data_ranges[n_th_vector]) >=
8627 std::get<0>(nonscalar_data_ranges[n_th_vector]),
8628 ExcLowerRange(std::get<1>(nonscalar_data_ranges[n_th_vector]),
8629 std::get<0>(nonscalar_data_ranges[n_th_vector])));
8631 std::get<1>(nonscalar_data_ranges[n_th_vector]) < n_data_sets,
8632 ExcIndexRange(std::get<1>(nonscalar_data_ranges[n_th_vector]),
8633 0,
8634 n_data_sets));
8635
8636 // Determine the vector name. Concatenate all the component names with
8637 // double underscores unless a vector name has been specified
8638 if (!std::get<2>(nonscalar_data_ranges[n_th_vector]).empty())
8639 {
8640 vector_name = std::get<2>(nonscalar_data_ranges[n_th_vector]);
8641 }
8642 else
8643 {
8644 vector_name = "";
8645 for (i = std::get<0>(nonscalar_data_ranges[n_th_vector]);
8646 i < std::get<1>(nonscalar_data_ranges[n_th_vector]);
8647 ++i)
8648 vector_name += data_names[i] + "__";
8649 vector_name +=
8650 data_names[std::get<1>(nonscalar_data_ranges[n_th_vector])];
8651 }
8652 }
8653 else
8654 {
8655 // One dimension
8656 pt_data_vector_dim = 1;
8657 vector_name = data_names[data_set];
8658 }
8659
8660 // Write data to the filter object
8661 filtered_data.write_data_set(vector_name,
8662 pt_data_vector_dim,
8663 data_set,
8664 data_vectors);
8665
8666 // Advance the current data set
8667 data_set += pt_data_vector_dim;
8668 }
8669}
8670
8671
8672
8673template <int dim, int spacedim>
8674void
8676 const DataOutBase::DataOutFilter &data_filter,
8677 const std::string &filename,
8678 const MPI_Comm comm) const
8679{
8681 get_patches(), data_filter, hdf5_flags, filename, comm);
8682}
8683
8684
8685
8686template <int dim, int spacedim>
8687void
8689 const DataOutBase::DataOutFilter &data_filter,
8690 const bool write_mesh_file,
8691 const std::string &mesh_filename,
8692 const std::string &solution_filename,
8693 const MPI_Comm comm) const
8694{
8696 data_filter,
8697 hdf5_flags,
8698 write_mesh_file,
8699 mesh_filename,
8700 solution_filename,
8701 comm);
8702}
8703
8704
8705
8706template <int dim, int spacedim>
8707void
8709 const std::vector<Patch<dim, spacedim>> &patches,
8710 const DataOutBase::DataOutFilter &data_filter,
8711 const DataOutBase::Hdf5Flags &flags,
8712 const std::string &filename,
8713 const MPI_Comm comm)
8714{
8716 patches, data_filter, flags, true, filename, filename, comm);
8717}
8718
8719
8720
8721template <int dim, int spacedim>
8722void
8724 const std::vector<Patch<dim, spacedim>> &patches,
8725 const DataOutBase::DataOutFilter &data_filter,
8726 const DataOutBase::Hdf5Flags &flags,
8727 const bool write_mesh_file,
8728 const std::string &mesh_filename,
8729 const std::string &solution_filename,
8730 const MPI_Comm comm)
8731{
8733 spacedim >= 2,
8734 ExcMessage(
8735 "DataOutBase was asked to write HDF5 output for a space dimension of 1. "
8736 "HDF5 only supports datasets that live in 2 or 3 dimensions."));
8737
8738#ifndef DEAL_II_WITH_HDF5
8739 // throw an exception, but first make sure the compiler does not warn about
8740 // the now unused function arguments
8741 (void)patches;
8742 (void)data_filter;
8743 (void)flags;
8744 (void)write_mesh_file;
8745 (void)mesh_filename;
8746 (void)solution_filename;
8747 (void)comm;
8748 AssertThrow(false, ExcNeedsHDF5());
8749#else
8750
8751 const unsigned int n_ranks = Utilities::MPI::n_mpi_processes(comm);
8752 (void)n_ranks;
8753
8754 // If HDF5 is not parallel and we're using multiple processes, abort:
8755# ifndef H5_HAVE_PARALLEL
8757 n_ranks <= 1,
8758 ExcMessage(
8759 "Serial HDF5 output on multiple processes is not yet supported."));
8760# endif
8761
8762 // Verify that there are indeed patches to be written out. most of
8763 // the times, people just forget to call build_patches when there
8764 // are no patches, so a warning is in order. That said, the
8765 // assertion is disabled if we run with more than one MPI rank,
8766 // since then it can happen that, on coarse meshes, a processor
8767 // simply has no cells it actually owns, and in that case it is
8768 // legit if there are no patches.
8769 Assert((patches.size() > 0) || (n_ranks > 1), ExcNoPatches());
8770
8771 // The HDF5 routines perform a bunch of collective calls that expect all
8772 // ranks to participate. One ranks without any patches we are missing
8773 // critical information, so rather than broadcasting that information, just
8774 // create a new communicator that only contains ranks with cells and
8775 // use that to perform the write operations:
8776 const bool have_patches = (patches.size() > 0);
8777 MPI_Comm split_comm;
8778 {
8779 const int key = Utilities::MPI::this_mpi_process(comm);
8780 const int color = (have_patches ? 1 : 0);
8781 const int ierr = MPI_Comm_split(comm, color, key, &split_comm);
8782 AssertThrowMPI(ierr);
8783 }
8784
8785 if (have_patches)
8786 {
8787 do_write_hdf5<dim, spacedim>(patches,
8788 data_filter,
8789 flags,
8790 write_mesh_file,
8791 mesh_filename,
8792 solution_filename,
8793 split_comm);
8794 }
8795
8796 const int ierr = MPI_Comm_free(&split_comm);
8797 AssertThrowMPI(ierr);
8798
8799#endif
8800}
8801
8802
8803
8804template <int dim, int spacedim>
8805void
8807 std::ostream &out,
8808 const DataOutBase::OutputFormat output_format_) const
8809{
8810 DataOutBase::OutputFormat output_format = output_format_;
8811 if (output_format == DataOutBase::default_format)
8812 output_format = default_fmt;
8813
8814 switch (output_format)
8815 {
8816 case DataOutBase::none:
8817 break;
8818
8819 case DataOutBase::dx:
8820 write_dx(out);
8821 break;
8822
8823 case DataOutBase::ucd:
8824 write_ucd(out);
8825 break;
8826
8828 write_gnuplot(out);
8829 break;
8830
8832 write_povray(out);
8833 break;
8834
8835 case DataOutBase::eps:
8836 write_eps(out);
8837 break;
8838
8839 case DataOutBase::gmv:
8840 write_gmv(out);
8841 break;
8842
8844 write_tecplot(out);
8845 break;
8846
8847 case DataOutBase::vtk:
8848 write_vtk(out);
8849 break;
8850
8851 case DataOutBase::vtu:
8852 write_vtu(out);
8853 break;
8854
8855 case DataOutBase::svg:
8856 write_svg(out);
8857 break;
8858
8860 write_deal_II_intermediate(out);
8861 break;
8862
8863 default:
8865 }
8866}
8867
8868
8869
8870template <int dim, int spacedim>
8871void
8878
8879template <int dim, int spacedim>
8880template <typename FlagType>
8881void
8883{
8884 if constexpr (std::is_same_v<FlagType, DataOutBase::DXFlags>)
8885 dx_flags = flags;
8886 else if constexpr (std::is_same_v<FlagType, DataOutBase::UcdFlags>)
8887 ucd_flags = flags;
8888 else if constexpr (std::is_same_v<FlagType, DataOutBase::PovrayFlags>)
8889 povray_flags = flags;
8890 else if constexpr (std::is_same_v<FlagType, DataOutBase::EpsFlags>)
8891 eps_flags = flags;
8892 else if constexpr (std::is_same_v<FlagType, DataOutBase::GmvFlags>)
8893 gmv_flags = flags;
8894 else if constexpr (std::is_same_v<FlagType, DataOutBase::Hdf5Flags>)
8895 hdf5_flags = flags;
8896 else if constexpr (std::is_same_v<FlagType, DataOutBase::TecplotFlags>)
8897 tecplot_flags = flags;
8898 else if constexpr (std::is_same_v<FlagType, DataOutBase::VtkFlags>)
8899 vtk_flags = flags;
8900 else if constexpr (std::is_same_v<FlagType, DataOutBase::SvgFlags>)
8901 svg_flags = flags;
8902 else if constexpr (std::is_same_v<FlagType, DataOutBase::GnuplotFlags>)
8903 gnuplot_flags = flags;
8904 else if constexpr (std::is_same_v<FlagType,
8906 deal_II_intermediate_flags = flags;
8907 else
8909}
8910
8911
8912
8913template <int dim, int spacedim>
8914std::string
8916 const DataOutBase::OutputFormat output_format) const
8917{
8918 if (output_format == DataOutBase::default_format)
8919 return DataOutBase::default_suffix(default_fmt);
8920 else
8921 return DataOutBase::default_suffix(output_format);
8922}
8923
8924
8925
8926template <int dim, int spacedim>
8927void
8929{
8930 prm.declare_entry("Output format",
8931 "gnuplot",
8933 "A name for the output format to be used");
8934 prm.declare_entry("Subdivisions",
8935 "1",
8937 "Number of subdivisions of each mesh cell");
8938
8939 prm.enter_subsection("DX output parameters");
8941 prm.leave_subsection();
8942
8943 prm.enter_subsection("UCD output parameters");
8945 prm.leave_subsection();
8946
8947 prm.enter_subsection("Gnuplot output parameters");
8949 prm.leave_subsection();
8950
8951 prm.enter_subsection("Povray output parameters");
8953 prm.leave_subsection();
8954
8955 prm.enter_subsection("Eps output parameters");
8957 prm.leave_subsection();
8958
8959 prm.enter_subsection("Gmv output parameters");
8961 prm.leave_subsection();
8962
8963 prm.enter_subsection("HDF5 output parameters");
8965 prm.leave_subsection();
8966
8967 prm.enter_subsection("Tecplot output parameters");
8969 prm.leave_subsection();
8970
8971 prm.enter_subsection("Vtk output parameters");
8973 prm.leave_subsection();
8974
8975
8976 prm.enter_subsection("deal.II intermediate output parameters");
8978 prm.leave_subsection();
8979}
8980
8981
8982
8983template <int dim, int spacedim>
8984void
8986{
8987 const std::string &output_name = prm.get("Output format");
8988 default_fmt = DataOutBase::parse_output_format(output_name);
8989 default_subdivisions = prm.get_integer("Subdivisions");
8990
8991 prm.enter_subsection("DX output parameters");
8992 dx_flags.parse_parameters(prm);
8993 prm.leave_subsection();
8994
8995 prm.enter_subsection("UCD output parameters");
8996 ucd_flags.parse_parameters(prm);
8997 prm.leave_subsection();
8998
8999 prm.enter_subsection("Gnuplot output parameters");
9000 gnuplot_flags.parse_parameters(prm);
9001 prm.leave_subsection();
9002
9003 prm.enter_subsection("Povray output parameters");
9004 povray_flags.parse_parameters(prm);
9005 prm.leave_subsection();
9006
9007 prm.enter_subsection("Eps output parameters");
9008 eps_flags.parse_parameters(prm);
9009 prm.leave_subsection();
9010
9011 prm.enter_subsection("Gmv output parameters");
9012 gmv_flags.parse_parameters(prm);
9013 prm.leave_subsection();
9014
9015 prm.enter_subsection("HDF5 output parameters");
9016 hdf5_flags.parse_parameters(prm);
9017 prm.leave_subsection();
9018
9019 prm.enter_subsection("Tecplot output parameters");
9020 tecplot_flags.parse_parameters(prm);
9021 prm.leave_subsection();
9022
9023 prm.enter_subsection("Vtk output parameters");
9024 vtk_flags.parse_parameters(prm);
9025 prm.leave_subsection();
9026
9027 prm.enter_subsection("deal.II intermediate output parameters");
9028 deal_II_intermediate_flags.parse_parameters(prm);
9029 prm.leave_subsection();
9030}
9031
9032
9033
9034template <int dim, int spacedim>
9035std::size_t
9051
9052
9053
9054template <int dim, int spacedim>
9055std::vector<
9056 std::tuple<unsigned int,
9057 unsigned int,
9058 std::string,
9061{
9062 return std::vector<
9063 std::tuple<unsigned int,
9064 unsigned int,
9065 std::string,
9067}
9068
9069
9070template <int dim, int spacedim>
9071void
9073{
9074 if constexpr (running_in_debug_mode())
9075 {
9076 {
9077 // Check that names for datasets are only used once. This is somewhat
9078 // complicated, because vector ranges might have a name or not.
9079 std::set<std::string> all_names;
9080
9081 const std::vector<
9082 std::tuple<unsigned int,
9083 unsigned int,
9084 std::string,
9086 ranges = this->get_nonscalar_data_ranges();
9087 const std::vector<std::string> data_names = this->get_dataset_names();
9088 const unsigned int n_data_sets = data_names.size();
9089 std::vector<bool> data_set_written(n_data_sets, false);
9090
9091 for (const auto &range : ranges)
9092 {
9093 const std::string &name = std::get<2>(range);
9094 if (!name.empty())
9095 {
9096 Assert(all_names.find(name) == all_names.end(),
9097 ExcMessage(
9098 "Error: names of fields in DataOut need to be unique, "
9099 "but '" +
9100 name + "' is used more than once."));
9101 all_names.insert(name);
9102 for (unsigned int i = std::get<0>(range);
9103 i <= std::get<1>(range);
9104 ++i)
9105 data_set_written[i] = true;
9106 }
9107 }
9108
9109 for (unsigned int data_set = 0; data_set < n_data_sets; ++data_set)
9110 if (data_set_written[data_set] == false)
9111 {
9112 const std::string &name = data_names[data_set];
9113 Assert(all_names.find(name) == all_names.end(),
9114 ExcMessage(
9115 "Error: names of fields in DataOut need to be unique, "
9116 "but '" +
9117 name + "' is used more than once."));
9118 all_names.insert(name);
9119 }
9120 }
9121 }
9122}
9123
9124
9125
9126// ---------------------------------------------- DataOutReader ----------
9127
9128template <int dim, int spacedim>
9129void
9131{
9132 AssertThrow(in.fail() == false, ExcIO());
9133
9134 // first empty previous content
9135 {
9136 std::vector<typename ::DataOutBase::Patch<dim, spacedim>> tmp;
9137 tmp.swap(patches);
9138 }
9139 {
9140 std::vector<std::string> tmp;
9141 tmp.swap(dataset_names);
9142 }
9143 {
9144 std::vector<
9145 std::tuple<unsigned int,
9146 unsigned int,
9147 std::string,
9149 tmp;
9150 tmp.swap(nonscalar_data_ranges);
9151 }
9152
9153 // then check that we have the correct header of this file. both the first and
9154 // second real lines have to match, as well as the dimension information
9155 // written before that and the Version information written in the third line
9156 {
9157 std::pair<unsigned int, unsigned int> dimension_info =
9159 AssertThrow((dimension_info.first == dim) &&
9160 (dimension_info.second == spacedim),
9161 ExcIncompatibleDimensions(
9162 dimension_info.first, dim, dimension_info.second, spacedim));
9163
9164 // read to the end of the line
9165 std::string tmp;
9166 getline(in, tmp);
9167 }
9168
9169 {
9170 std::string header;
9171 getline(in, header);
9172
9173 std::ostringstream s;
9174 s << "[deal.II intermediate format graphics data]";
9175
9176 Assert(header == s.str(), ExcUnexpectedInput(s.str(), header));
9177 }
9178 {
9179 std::string header;
9180 getline(in, header);
9181
9182 std::ostringstream s;
9183 s << "[written by " << DEAL_II_PACKAGE_NAME << " "
9184 << DEAL_II_PACKAGE_VERSION << "]";
9185
9186 Assert(header == s.str(), ExcUnexpectedInput(s.str(), header));
9187 }
9188 {
9189 std::string header;
9190 getline(in, header);
9191
9192 std::ostringstream s;
9193 s << "[Version: "
9195
9196 Assert(header == s.str(),
9197 ExcMessage(
9198 "Invalid or incompatible file format. Intermediate format "
9199 "files can only be read by the same deal.II version as they "
9200 "are written by."));
9201 }
9202
9203 // then read the rest of the data
9204 unsigned int n_datasets;
9205 in >> n_datasets;
9206 dataset_names.resize(n_datasets);
9207 for (unsigned int i = 0; i < n_datasets; ++i)
9208 in >> dataset_names[i];
9209
9210 unsigned int n_patches;
9211 in >> n_patches;
9212 patches.resize(n_patches);
9213 for (unsigned int i = 0; i < n_patches; ++i)
9214 in >> patches[i];
9215
9216 unsigned int n_nonscalar_data_ranges;
9217 in >> n_nonscalar_data_ranges;
9218 nonscalar_data_ranges.resize(n_nonscalar_data_ranges);
9219 for (unsigned int i = 0; i < n_nonscalar_data_ranges; ++i)
9220 {
9221 in >> std::get<0>(nonscalar_data_ranges[i]) >>
9222 std::get<1>(nonscalar_data_ranges[i]);
9223
9224 // read in the name of that vector range. because it is on a separate
9225 // line, we first need to read to the end of the previous line (nothing
9226 // should be there any more after we've read the previous two integers)
9227 // and then read the entire next line for the name
9228 std::string name;
9229 getline(in, name);
9230 getline(in, name);
9231 std::get<2>(nonscalar_data_ranges[i]) = name;
9232 }
9233
9234 AssertThrow(in.fail() == false, ExcIO());
9235}
9236
9237
9238
9239template <int dim, int spacedim>
9240void
9242{
9243 AssertThrow(in.fail() == false, ExcIO());
9244
9245 ParallelIntermediateHeader header;
9246 in.read(reinterpret_cast<char *>(&header), sizeof(header));
9248 header.magic == 0x00dea111,
9249 ExcMessage(
9250 "Invalid header of parallel deal.II intermediate format encountered."));
9253 ExcMessage(
9254 "Incorrect header version of parallel deal.II intermediate format."));
9255
9256 std::vector<std::uint64_t> chunk_sizes(header.n_ranks);
9257 in.read(reinterpret_cast<char *>(chunk_sizes.data()),
9258 header.n_ranks * sizeof(std::uint64_t));
9259
9260 for (unsigned int n = 0; n < header.n_ranks; ++n)
9261 {
9262 // First read the compressed data into temp_buffer and then
9263 // decompress and put into datastream
9264 std::vector<char> temp_buffer(chunk_sizes[n]);
9265 in.read(temp_buffer.data(), chunk_sizes[n]);
9266
9268 header.compression) !=
9271
9272 boost::iostreams::filtering_istreambuf f;
9273 if (static_cast<DataOutBase::CompressionLevel>(header.compression) !=
9275#ifdef DEAL_II_WITH_ZLIB
9276 f.push(boost::iostreams::zlib_decompressor());
9277#else
9279 false,
9280 ExcMessage(
9281 "Decompression requires deal.II to be configured with ZLIB support."));
9282#endif
9283
9284 boost::iostreams::basic_array_source<char> source(temp_buffer.data(),
9285 temp_buffer.size());
9286 f.push(source);
9287
9288 std::stringstream datastream;
9289 boost::iostreams::copy(f, datastream);
9290
9291 // Now we can load the data and merge this chunk into *this
9292 if (n == 0)
9293 {
9294 read(datastream);
9295 }
9296 else
9297 {
9298 DataOutReader<dim, spacedim> temp_reader;
9299 temp_reader.read(datastream);
9300 merge(temp_reader);
9301 }
9302 }
9303}
9304
9305
9306
9307template <int dim, int spacedim>
9308void
9310{
9311 using Patch = typename ::DataOutBase::Patch<dim, spacedim>;
9312
9313
9314 const std::vector<Patch> &source_patches = source.get_patches();
9315 Assert(patches.size() != 0, DataOutBase::ExcNoPatches());
9316 Assert(source_patches.size() != 0, DataOutBase::ExcNoPatches());
9317 // check equality of component names
9318 Assert(get_dataset_names() == source.get_dataset_names(),
9319 ExcIncompatibleDatasetNames());
9320
9321 // check equality of the vector data specifications
9322 Assert(get_nonscalar_data_ranges().size() ==
9323 source.get_nonscalar_data_ranges().size(),
9324 ExcMessage("Both sources need to declare the same components "
9325 "as vectors."));
9326 for (unsigned int i = 0; i < get_nonscalar_data_ranges().size(); ++i)
9327 {
9328 Assert(std::get<0>(get_nonscalar_data_ranges()[i]) ==
9329 std::get<0>(source.get_nonscalar_data_ranges()[i]),
9330 ExcMessage("Both sources need to declare the same components "
9331 "as vectors."));
9332 Assert(std::get<1>(get_nonscalar_data_ranges()[i]) ==
9333 std::get<1>(source.get_nonscalar_data_ranges()[i]),
9334 ExcMessage("Both sources need to declare the same components "
9335 "as vectors."));
9336 Assert(std::get<2>(get_nonscalar_data_ranges()[i]) ==
9337 std::get<2>(source.get_nonscalar_data_ranges()[i]),
9338 ExcMessage("Both sources need to declare the same components "
9339 "as vectors."));
9340 }
9341
9342 // make sure patches are compatible
9343 Assert(patches[0].n_subdivisions == source_patches[0].n_subdivisions,
9344 ExcIncompatiblePatchLists());
9345 Assert(patches[0].data.n_rows() == source_patches[0].data.n_rows(),
9346 ExcIncompatiblePatchLists());
9347 Assert(patches[0].data.n_cols() == source_patches[0].data.n_cols(),
9348 ExcIncompatiblePatchLists());
9349
9350 // merge patches. store old number of elements, since we need to adjust patch
9351 // numbers, etc afterwards
9352 const unsigned int old_n_patches = patches.size();
9353 patches.insert(patches.end(), source_patches.begin(), source_patches.end());
9354
9355 // adjust patch numbers
9356 for (unsigned int i = old_n_patches; i < patches.size(); ++i)
9357 patches[i].patch_index += old_n_patches;
9358
9359 // adjust patch neighbors
9360 for (unsigned int i = old_n_patches; i < patches.size(); ++i)
9361 for (const unsigned int n : GeometryInfo<dim>::face_indices())
9362 if (patches[i].neighbors[n] !=
9364 patches[i].neighbors[n] += old_n_patches;
9365}
9366
9367
9368
9369template <int dim, int spacedim>
9370const std::vector<typename ::DataOutBase::Patch<dim, spacedim>> &
9372{
9373 return patches;
9374}
9375
9376
9377
9378template <int dim, int spacedim>
9379std::vector<std::string>
9381{
9382 return dataset_names;
9383}
9384
9385
9386
9387template <int dim, int spacedim>
9388std::vector<
9389 std::tuple<unsigned int,
9390 unsigned int,
9391 std::string,
9394{
9395 return nonscalar_data_ranges;
9396}
9397
9398
9399
9400// ---------------------------------------------- XDMFEntry ----------
9401
9403 : valid(false)
9404 , h5_sol_filename("")
9405 , h5_mesh_filename("")
9406 , entry_time(0.0)
9407 , num_nodes(numbers::invalid_unsigned_int)
9408 , num_cells(numbers::invalid_unsigned_int)
9409 , dimension(numbers::invalid_unsigned_int)
9410 , space_dimension(numbers::invalid_unsigned_int)
9411 , cell_type()
9412{}
9413
9414
9415
9416XDMFEntry::XDMFEntry(const std::string &filename,
9417 const double time,
9418 const std::uint64_t nodes,
9419 const std::uint64_t cells,
9420 const unsigned int dim,
9421 const ReferenceCell &cell_type)
9422 : XDMFEntry(filename, filename, time, nodes, cells, dim, dim, cell_type)
9423{}
9424
9425
9426
9427XDMFEntry::XDMFEntry(const std::string &mesh_filename,
9428 const std::string &solution_filename,
9429 const double time,
9430 const std::uint64_t nodes,
9431 const std::uint64_t cells,
9432 const unsigned int dim,
9433 const ReferenceCell &cell_type)
9434 : XDMFEntry(mesh_filename,
9435 solution_filename,
9436 time,
9437 nodes,
9438 cells,
9439 dim,
9440 dim,
9441 cell_type)
9442{}
9443
9444
9445
9446namespace
9447{
9453 cell_type_hex_if_invalid(const ReferenceCell &cell_type,
9454 const unsigned int dimension)
9455 {
9456 if (cell_type == ReferenceCells::Invalid)
9457 {
9458 switch (dimension)
9459 {
9460 case 0:
9461 return ReferenceCells::get_hypercube<0>();
9462 case 1:
9463 return ReferenceCells::get_hypercube<1>();
9464 case 2:
9465 return ReferenceCells::get_hypercube<2>();
9466 case 3:
9467 return ReferenceCells::get_hypercube<3>();
9468 default:
9469 AssertThrow(false, ExcMessage("Invalid dimension"));
9470 }
9471 }
9472 else
9473 return cell_type;
9474 }
9475} // namespace
9476
9477
9478
9479XDMFEntry::XDMFEntry(const std::string &mesh_filename,
9480 const std::string &solution_filename,
9481 const double time,
9482 const std::uint64_t nodes,
9483 const std::uint64_t cells,
9484 const unsigned int dim,
9485 const unsigned int spacedim,
9486 const ReferenceCell &cell_type_)
9487 : valid(true)
9488 , h5_sol_filename(solution_filename)
9489 , h5_mesh_filename(mesh_filename)
9490 , entry_time(time)
9491 , num_nodes(nodes)
9492 , num_cells(cells)
9493 , dimension(dim)
9494 , space_dimension(spacedim)
9495 , cell_type(cell_type_hex_if_invalid(cell_type_, dim))
9496{}
9497
9498
9499
9500void
9501XDMFEntry::add_attribute(const std::string &attr_name,
9502 const unsigned int dimension)
9503{
9504 attribute_dims[attr_name] = dimension;
9505}
9506
9507
9508
9509namespace
9510{
9514 std::string
9515 indent(const unsigned int indent_level)
9516 {
9517 std::string res = "";
9518 for (unsigned int i = 0; i < indent_level; ++i)
9519 res += " ";
9520 return res;
9521 }
9522} // namespace
9523
9524
9525
9526std::string
9527XDMFEntry::get_xdmf_content(const unsigned int indent_level) const
9528{
9529 if (!valid)
9530 return "";
9531
9532 std::stringstream ss;
9533 ss.precision(12);
9534 ss << indent(indent_level + 0)
9535 << "<Grid Name=\"mesh\" GridType=\"Uniform\">\n";
9536 ss << indent(indent_level + 1) << "<Time Value=\"" << entry_time << "\"/>\n";
9537 ss << indent(indent_level + 1) << "<Geometry GeometryType=\""
9538 << (space_dimension <= 2 ? "XY" : "XYZ") << "\">\n";
9539 ss << indent(indent_level + 2) << "<DataItem Dimensions=\"" << num_nodes
9540 << " " << (space_dimension <= 2 ? 2 : space_dimension)
9541 << "\" NumberType=\"Float\" Precision=\"8\" Format=\"HDF\">\n";
9542 ss << indent(indent_level + 3) << h5_mesh_filename << ":/nodes\n";
9543 ss << indent(indent_level + 2) << "</DataItem>\n";
9544 ss << indent(indent_level + 1) << "</Geometry>\n";
9545
9546 // If we have cells defined, use the topology corresponding to the dimension
9547 if (num_cells > 0)
9548 {
9549 ss << indent(indent_level + 1) << "<Topology TopologyType=\"";
9550
9551 if (dimension == 0)
9552 {
9553 ss << "Polyvertex";
9554 }
9555 else if (dimension == 1)
9556 {
9557 ss << "Polyline";
9558 }
9559 else if (dimension == 2)
9560 {
9564
9566 {
9567 ss << "Quadrilateral";
9568 }
9569 else // if (cell_type == ReferenceCells::Triangle)
9570 {
9571 ss << "Triangle";
9572 }
9573 }
9574 else if (dimension == 3)
9575 {
9579
9581 {
9582 ss << "Hexahedron";
9583 }
9584 else // if (reference_cell == ReferenceCells::Tetrahedron)
9585 {
9586 ss << "Tetrahedron";
9587 }
9588 }
9589
9590 ss << "\" NumberOfElements=\"" << num_cells;
9591 if (dimension == 0)
9592 ss << "\" NodesPerElement=\"1\">\n";
9593 else if (dimension == 1)
9594 ss << "\" NodesPerElement=\"2\">\n";
9595 else
9596 // no "NodesPerElement" for dimension 2 and higher
9597 ss << "\">\n";
9598
9599 ss << indent(indent_level + 2) << "<DataItem Dimensions=\"" << num_cells
9600 << " " << cell_type.n_vertices()
9601 << "\" NumberType=\"UInt\" Format=\"HDF\">\n";
9602
9603 ss << indent(indent_level + 3) << h5_mesh_filename << ":/cells\n";
9604 ss << indent(indent_level + 2) << "</DataItem>\n";
9605 ss << indent(indent_level + 1) << "</Topology>\n";
9606 }
9607 // Otherwise, we assume the points are isolated in space and use a Polyvertex
9608 // topology
9609 else
9610 {
9611 ss << indent(indent_level + 1)
9612 << "<Topology TopologyType=\"Polyvertex\" NumberOfElements=\""
9613 << num_nodes << "\">\n";
9614 ss << indent(indent_level + 1) << "</Topology>\n";
9615 }
9616
9617 for (const auto &attribute_dim : attribute_dims)
9618 {
9619 ss << indent(indent_level + 1) << "<Attribute Name=\""
9620 << attribute_dim.first << "\" AttributeType=\""
9621 << (attribute_dim.second > 1 ? "Vector" : "Scalar")
9622 << "\" Center=\"Node\">\n";
9623 // Vectors must have 3 elements even for 2d models
9624 ss << indent(indent_level + 2) << "<DataItem Dimensions=\"" << num_nodes
9625 << " " << (attribute_dim.second > 1 ? 3 : 1)
9626 << "\" NumberType=\"Float\" Precision=\"8\" Format=\"HDF\">\n";
9627 ss << indent(indent_level + 3) << h5_sol_filename << ":/"
9628 << attribute_dim.first << '\n';
9629 ss << indent(indent_level + 2) << "</DataItem>\n";
9630 ss << indent(indent_level + 1) << "</Attribute>\n";
9631 }
9632
9633 ss << indent(indent_level + 0) << "</Grid>\n";
9634
9635 return ss.str();
9636}
9637
9638
9639
9640namespace DataOutBase
9641{
9642 template <int dim, int spacedim>
9643 std::ostream &
9644 operator<<(std::ostream &out, const Patch<dim, spacedim> &patch)
9645 {
9646 // write a header line
9647 out << "[deal.II intermediate Patch<" << dim << ',' << spacedim << ">]"
9648 << '\n';
9649
9650 // First export what kind of reference cell we are looking at:
9651 out << patch.reference_cell << '\n';
9652
9653 // then write all the data that is in this patch
9654 for (const unsigned int i : patch.reference_cell.vertex_indices())
9655 out << patch.vertices[i] << ' ';
9656 out << '\n';
9657
9658 for (const unsigned int i : patch.reference_cell.face_indices())
9659 out << patch.neighbors[i] << ' ';
9660 out << '\n';
9661
9662 out << patch.patch_index << ' ' << patch.n_subdivisions << '\n';
9663
9664 out << patch.points_are_available << '\n';
9665
9666 out << patch.data.n_rows() << ' ' << patch.data.n_cols() << '\n';
9667 for (unsigned int i = 0; i < patch.data.n_rows(); ++i)
9668 for (unsigned int j = 0; j < patch.data.n_cols(); ++j)
9669 out << patch.data[i][j] << ' ';
9670 out << '\n';
9671 out << '\n';
9672
9673 return out;
9674 }
9675
9676
9677
9678 template <int dim, int spacedim>
9679 std::istream &
9680 operator>>(std::istream &in, Patch<dim, spacedim> &patch)
9681 {
9682 AssertThrow(in.fail() == false, ExcIO());
9683
9684 // read a header line and compare it to what we usually write. skip all
9685 // lines that contain only blanks at the start
9686 {
9687 std::string header;
9688 do
9689 {
9690 getline(in, header);
9691 while ((header.size() != 0) && (header.back() == ' '))
9692 header.erase(header.size() - 1);
9693 }
9694 while ((header.empty()) && in);
9695
9696 std::ostringstream s;
9697 s << "[deal.II intermediate Patch<" << dim << ',' << spacedim << ">]";
9698
9699 Assert(header == s.str(), ExcUnexpectedInput(s.str(), header));
9700 }
9701
9702 // First import what kind of reference cell we are looking at:
9703 if constexpr (dim > 0)
9704 in >> patch.reference_cell;
9705
9706 // then read all the data that is in this patch
9707 for (const unsigned int i : patch.reference_cell.vertex_indices())
9708 in >> patch.vertices[i];
9709
9710 for (const unsigned int i : patch.reference_cell.face_indices())
9711 in >> patch.neighbors[i];
9712
9713 in >> patch.patch_index;
9714
9715 // If dim>1, we also need to set the number of subdivisions, whereas
9716 // in dim==1, this is a const variable equal to one that can't be changed.
9717 unsigned int n_subdivisions;
9718 in >> n_subdivisions;
9719 if constexpr (dim > 1)
9720 patch.n_subdivisions = n_subdivisions;
9721
9722 in >> patch.points_are_available;
9723
9724 unsigned int n_rows, n_cols;
9725 in >> n_rows >> n_cols;
9726 patch.data.reinit(n_rows, n_cols);
9727 for (unsigned int i = 0; i < patch.data.n_rows(); ++i)
9728 for (unsigned int j = 0; j < patch.data.n_cols(); ++j)
9729 in >> patch.data[i][j];
9730
9731 AssertThrow(in.fail() == false, ExcIO());
9732
9733 return in;
9734 }
9735} // namespace DataOutBase
9736
9737
9738
9739// explicit instantiations
9740#include "base/data_out_base.inst"
9741
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
Definition point.h:113
std_cxx20::ranges::iota_view< unsigned int, unsigned int > vertex_indices() const
unsigned int n_vertices() const
bool is_hyper_cube() const
unsigned int vtk_linear_type() const
unsigned int vtk_vertex_to_deal_vertex(const unsigned int vertex_index) const
unsigned int vtk_quadratic_type() const
unsigned int vtk_lagrange_type() const
bool is_simplex() const
std_cxx20::ranges::iota_view< unsigned int, unsigned int > face_indices() const
static constexpr TableIndices< rank_ > unrolled_to_component_indices(const unsigned int i)
std::vector< RT > return_values()
internal::return_value< RT >::reference_type return_value()
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:35
#define DEAL_II_PACKAGE_VERSION
Definition config.h:24
constexpr bool running_in_debug_mode()
Definition config.h:73
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:36
#define DEAL_II_FALLTHROUGH
Definition config.h:267
#define DEAL_II_PACKAGE_NAME
Definition config.h:22
std::ostream & operator<<(std::ostream &out, const DerivativeForm< order, dim, spacedim, Number > &df)
#define DEAL_II_ASSERT_UNREACHABLE()
#define DEAL_II_NOT_IMPLEMENTED()
unsigned int level
Definition grid_out.cc:4635
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)
#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)
const unsigned int my_rank
Definition mpi.cc:929
std::vector< index_type > data
Definition mpi.cc:746
std::size_t size
Definition mpi.cc:745
const MPI_Comm comm
Definition mpi.cc:924
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 ReferenceCell Triangle
constexpr ReferenceCell Hexahedron
constexpr ReferenceCell Pyramid
constexpr ReferenceCell Wedge
constexpr ReferenceCell Vertex
constexpr ReferenceCell Invalid
constexpr ReferenceCell Quadrilateral
constexpr ReferenceCell Tetrahedron
constexpr 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:99
unsigned int this_mpi_process(const MPI_Comm mpi_communicator)
Definition mpi.cc:114
void free_communicator(MPI_Comm mpi_communicator)
Definition mpi.cc:161
std::string get_time()
std::string get_date()
std::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:427
std::string int_to_string(const unsigned int value, const unsigned int digits=numbers::invalid_unsigned_int)
Definition utilities.cc:466
unsigned int needed_digits(const unsigned int max_number)
Definition utilities.cc:561
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:14904
constexpr double PI
Definition numbers.h:240
constexpr unsigned int invalid_unsigned_int
Definition types.h:238
::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
RgbValues(*)(const double value, const double min_value, const double max_value) ColorFunction
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
unsigned int patch_index
ReferenceCell reference_cell
Table< 2, float > data
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< Point< spacedim >, GeometryInfo< dim >::vertices_per_cell > vertices
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)