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