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