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