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