deal.II version GIT relicensing-1873-gbe302a5b2e 2024-09-18 11:00:00+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
data_out_base.cc
Go to the documentation of this file.
1// ------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: LGPL-2.1-or-later
4// Copyright (C) 1999 - 2024 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Part of the source code is dual licensed under Apache-2.0 WITH
9// LLVM-exception OR LGPL-2.1-or-later. Detailed license information
10// governing the source code and code contributions can be found in
11// LICENSE.md and CONTRIBUTING.md at the top level directory of deal.II.
12//
13// ------------------------------------------------------------------------
14
15
18#include <deal.II/base/mpi.h>
23
25
26#include <algorithm>
27#include <cmath>
28#include <cstdint>
29#include <cstring>
30#include <ctime>
31#include <fstream>
32#include <iomanip>
33#include <limits>
34#include <memory>
35#include <set>
36#include <sstream>
37#include <vector>
38
39#ifdef DEAL_II_WITH_ZLIB
40# include <zlib.h>
41#endif
42
43#ifdef DEAL_II_WITH_HDF5
44# include <hdf5.h>
45#endif
46
47#include <boost/iostreams/copy.hpp>
48#include <boost/iostreams/device/back_inserter.hpp>
49#include <boost/iostreams/filtering_stream.hpp>
50#ifdef DEAL_II_WITH_ZLIB
51# include <boost/iostreams/filter/zlib.hpp>
52#endif
53
54
55
57
58// we need the following exception from a global function, so can't declare it
59// in the usual way inside a class
60namespace
61{
62 DeclException2(ExcUnexpectedInput,
63 std::string,
64 std::string,
65 << "Unexpected input: expected line\n <" << arg1
66 << ">\nbut got\n <" << arg2 << ">");
67}
68
69
70namespace
71{
72#ifdef DEAL_II_WITH_ZLIB
73 constexpr bool deal_ii_with_zlib = true;
74#else
75 constexpr bool deal_ii_with_zlib = false;
76#endif
77
78
79#ifdef DEAL_II_WITH_ZLIB
84 int
85 get_zlib_compression_level(const DataOutBase::CompressionLevel level)
86 {
87 switch (level)
88 {
90 return Z_NO_COMPRESSION;
92 return Z_BEST_SPEED;
94 return Z_BEST_COMPRESSION;
96 return Z_DEFAULT_COMPRESSION;
97 default:
99 return Z_NO_COMPRESSION;
100 }
101 }
102
103# ifdef DEAL_II_WITH_MPI
108 int
109 get_boost_zlib_compression_level(const DataOutBase::CompressionLevel level)
110 {
111 switch (level)
112 {
114 return boost::iostreams::zlib::no_compression;
116 return boost::iostreams::zlib::best_speed;
118 return boost::iostreams::zlib::best_compression;
120 return boost::iostreams::zlib::default_compression;
121 default:
123 return boost::iostreams::zlib::no_compression;
124 }
125 }
126# endif
127#endif
128
133 template <typename T>
134 std::string
135 compress_array(const std::vector<T> &data,
136 const DataOutBase::CompressionLevel compression_level)
137 {
138#ifdef DEAL_II_WITH_ZLIB
139 if (data.size() != 0)
140 {
141 const std::size_t uncompressed_size = (data.size() * sizeof(T));
142
143 // While zlib's compress2 uses unsigned long (which is 64bits
144 // on Linux), the vtu compression header stores the block size
145 // as an std::uint32_t (see below). While we could implement
146 // writing several smaller blocks, we haven't done that. Let's
147 // trigger an error for the user instead:
148 AssertThrow(uncompressed_size <=
149 std::numeric_limits<std::uint32_t>::max(),
151
152 // allocate a buffer for compressing data and do so
153 auto compressed_data_length = compressBound(uncompressed_size);
154 AssertThrow(compressed_data_length <=
155 std::numeric_limits<std::uint32_t>::max(),
157
158 std::vector<unsigned char> compressed_data(compressed_data_length);
159
160 int err = compress2(&compressed_data[0],
161 &compressed_data_length,
162 reinterpret_cast<const Bytef *>(data.data()),
163 uncompressed_size,
164 get_zlib_compression_level(compression_level));
165 (void)err;
166 Assert(err == Z_OK, ExcInternalError());
167
168 // Discard the unnecessary bytes
169 compressed_data.resize(compressed_data_length);
170
171 // now encode the compression header
172 const std::uint32_t compression_header[4] = {
173 1, /* number of blocks */
174 static_cast<std::uint32_t>(uncompressed_size), /* size of block */
175 static_cast<std::uint32_t>(
176 uncompressed_size), /* size of last block */
177 static_cast<std::uint32_t>(
178 compressed_data_length)}; /* list of compressed sizes of blocks */
179
180 const auto *const header_start =
181 reinterpret_cast<const unsigned char *>(&compression_header[0]);
182
184 {header_start, header_start + 4 * sizeof(std::uint32_t)}) +
185 Utilities::encode_base64(compressed_data));
186 }
187 else
188 return {};
189#else
190 (void)data;
191 (void)compression_level;
192 Assert(false,
193 ExcMessage("This function can only be called if cmake found "
194 "a working libz installation."));
195 return {};
196#endif
197 }
198
199
200
209 template <typename T>
210 std::string
211 vtu_stringize_array(const std::vector<T> &data,
212 const DataOutBase::CompressionLevel compression_level,
213 const int precision)
214 {
215 if (deal_ii_with_zlib &&
216 (compression_level != DataOutBase::CompressionLevel::plain_text))
217 {
218 // compress the data we have in memory
219 return compress_array(data, compression_level);
220 }
221 else
222 {
223 std::ostringstream stream;
224 stream.precision(precision);
225 for (const T &el : data)
226 stream << el << ' ';
227 return stream.str();
228 }
229 }
230
231
240 struct ParallelIntermediateHeader
241 {
242 std::uint64_t magic;
243 std::uint64_t version;
244 std::uint64_t compression;
245 std::uint64_t dimension;
246 std::uint64_t space_dimension;
247 std::uint64_t n_ranks;
248 std::uint64_t n_patches;
249 };
250} // namespace
251
252
253// some declarations of functions and locally used classes
254namespace DataOutBase
255{
256 namespace
257 {
263 class SvgCell
264 {
265 public:
266 // Center of the cell (three-dimensional)
268
273
278 float depth;
279
284
285 // Center of the cell (projected, two-dimensional)
287
291 bool
292 operator<(const SvgCell &) const;
293 };
294
295 bool
296 SvgCell::operator<(const SvgCell &e) const
297 {
298 // note the "wrong" order in which we sort the elements
299 return depth > e.depth;
300 }
301
302
303
309 class EpsCell2d
310 {
311 public:
316
322
327 float depth;
328
332 bool
333 operator<(const EpsCell2d &) const;
334 };
335
336 bool
337 EpsCell2d::operator<(const EpsCell2d &e) const
338 {
339 // note the "wrong" order in which we sort the elements
340 return depth > e.depth;
341 }
342
343
344
356 template <int dim, int spacedim, typename Number = double>
357 std::unique_ptr<Table<2, Number>>
358 create_global_data_table(const std::vector<Patch<dim, spacedim>> &patches)
360 // If there is nothing to write, just return
361 if (patches.empty())
362 return std::make_unique<Table<2, Number>>();
363
364 // unlike in the main function, we don't have here the data_names field,
365 // so we initialize it with the number of data sets in the first patch.
366 // the equivalence of these two definitions is checked in the main
367 // function.
369 // we have to take care, however, whether the points are appended to the
370 // end of the patch.data table
371 const unsigned int n_data_sets = patches[0].points_are_available ?
372 (patches[0].data.n_rows() - spacedim) :
373 patches[0].data.n_rows();
374 const unsigned int n_data_points =
375 std::accumulate(patches.begin(),
376 patches.end(),
377 0U,
378 [](const unsigned int count,
379 const Patch<dim, spacedim> &patch) {
380 return count + patch.data.n_cols();
381 });
382
383 std::unique_ptr<Table<2, Number>> global_data_table =
384 std::make_unique<Table<2, Number>>(n_data_sets, n_data_points);
385
386 // loop over all patches
387 unsigned int next_value = 0;
388 for (const auto &patch : patches)
389 {
390 const unsigned int n_subdivisions = patch.n_subdivisions;
391 (void)n_subdivisions;
392
393 Assert((patch.data.n_rows() == n_data_sets &&
394 !patch.points_are_available) ||
395 (patch.data.n_rows() == n_data_sets + spacedim &&
396 patch.points_are_available),
397 ExcDimensionMismatch(patch.points_are_available ?
398 (n_data_sets + spacedim) :
399 n_data_sets,
400 patch.data.n_rows()));
401 Assert(patch.reference_cell != ReferenceCells::get_hypercube<dim>() ||
402 (n_data_sets == 0) ||
403 (patch.data.n_cols() ==
404 Utilities::fixed_power<dim>(n_subdivisions + 1)),
405 ExcInvalidDatasetSize(patch.data.n_cols(),
406 n_subdivisions + 1));
407
408 for (unsigned int i = 0; i < patch.data.n_cols(); ++i, ++next_value)
409 for (unsigned int data_set = 0; data_set < n_data_sets; ++data_set)
410 (*global_data_table)[data_set][next_value] =
411 patch.data(data_set, i);
412 }
413 Assert(next_value == n_data_points, ExcInternalError());
414
415 return global_data_table;
416 }
417 } // namespace
418
419
420
422 : flags(false, true)
423 , node_dim(numbers::invalid_unsigned_int)
424 , num_cells(0)
425 {}
426
427
428
430 : flags(flags)
431 , node_dim(numbers::invalid_unsigned_int)
432 , num_cells(0)
433 {}
434
435
436
437 template <int dim>
438 void
439 DataOutFilter::write_point(const unsigned int index, const Point<dim> &p)
440 {
441 node_dim = dim;
442
443 Point<3> int_pt;
444 for (unsigned int d = 0; d < dim; ++d)
445 int_pt[d] = p[d];
446
447 const Map3DPoint::const_iterator it = existing_points.find(int_pt);
448 unsigned int internal_ind;
449
450 // If the point isn't in the set, or we're not filtering duplicate points,
451 // add it
453 {
454 internal_ind = existing_points.size();
455 existing_points.insert(std::make_pair(int_pt, internal_ind));
456 }
457 else
458 {
459 internal_ind = it->second;
460 }
461 // Now add the index to the list of filtered points
462 filtered_points[index] = internal_ind;
463 }
464
465
466
467 void
469 const unsigned int pt_index)
470 {
472
473 // (Re)-initialize counter at any first call to this method.
474 if (cell_index == 0)
475 num_cells = 1;
476 }
477
478
479
480 void
481 DataOutFilter::fill_node_data(std::vector<double> &node_data) const
482 {
483 node_data.resize(existing_points.size() * node_dim);
484
485 for (const auto &existing_point : existing_points)
486 {
487 for (unsigned int d = 0; d < node_dim; ++d)
488 node_data[node_dim * existing_point.second + d] =
489 existing_point.first[d];
490 }
491 }
492
493
494
495 void
496 DataOutFilter::fill_cell_data(const unsigned int local_node_offset,
497 std::vector<unsigned int> &cell_data) const
498 {
499 cell_data.resize(filtered_cells.size());
500
501 for (const auto &filtered_cell : filtered_cells)
502 {
503 cell_data[filtered_cell.first] =
504 filtered_cell.second + local_node_offset;
505 }
506 }
507
508
509
510 std::string
511 DataOutFilter::get_data_set_name(const unsigned int set_num) const
512 {
513 return data_set_names.at(set_num);
514 }
515
516
517
518 unsigned int
519 DataOutFilter::get_data_set_dim(const unsigned int set_num) const
520 {
521 return data_set_dims.at(set_num);
522 }
523
524
525
526 const double *
527 DataOutFilter::get_data_set(const unsigned int set_num) const
528 {
529 return data_sets[set_num].data();
530 }
531
532
533
534 unsigned int
536 {
537 return existing_points.size();
538 }
539
540
541
542 unsigned int
544 {
545 return num_cells;
546 }
547
548
549
550 unsigned int
552 {
553 return data_set_names.size();
554 }
555
556
557
558 void
561
562
563
564 void
567
568
569
570 template <int dim>
571 void
572 DataOutFilter::write_cell(const unsigned int index,
573 const unsigned int start,
574 const std::array<unsigned int, dim> &offsets)
575 {
576 ++num_cells;
577
578 const unsigned int base_entry =
580
581 switch (dim)
582 {
583 case 0:
584 {
585 internal_add_cell(base_entry + 0, start);
586 break;
587 }
588
589 case 1:
590 {
591 const unsigned int d1 = offsets[0];
592
593 internal_add_cell(base_entry + 0, start);
594 internal_add_cell(base_entry + 1, start + d1);
595 break;
596 }
597
598 case 2:
599 {
600 const unsigned int d1 = offsets[0];
601 const unsigned int d2 = offsets[1];
602
603 internal_add_cell(base_entry + 0, start);
604 internal_add_cell(base_entry + 1, start + d1);
605 internal_add_cell(base_entry + 2, start + d2 + d1);
606 internal_add_cell(base_entry + 3, start + d2);
607 break;
608 }
609
610 case 3:
611 {
612 const unsigned int d1 = offsets[0];
613 const unsigned int d2 = offsets[1];
614 const unsigned int d3 = offsets[2];
615
616 internal_add_cell(base_entry + 0, start);
617 internal_add_cell(base_entry + 1, start + d1);
618 internal_add_cell(base_entry + 2, start + d2 + d1);
619 internal_add_cell(base_entry + 3, start + d2);
620 internal_add_cell(base_entry + 4, start + d3);
621 internal_add_cell(base_entry + 5, start + d3 + d1);
622 internal_add_cell(base_entry + 6, start + d3 + d2 + d1);
623 internal_add_cell(base_entry + 7, start + d3 + d2);
624 break;
625 }
626
627 default:
629 }
630 }
631
632
633
634 void
635 DataOutFilter::write_cell_single(const unsigned int index,
636 const unsigned int start,
637 const unsigned int n_points,
638 const ReferenceCell &reference_cell)
639 {
640 ++num_cells;
641
642 const unsigned int base_entry = index * n_points;
643
644 static const std::array<unsigned int, 5> table = {{0, 1, 3, 2, 4}};
645
646 for (unsigned int i = 0; i < n_points; ++i)
647 internal_add_cell(base_entry + i,
648 start + (reference_cell == ReferenceCells::Pyramid ?
649 table[i] :
650 i));
651 }
652
653
654
655 void
656 DataOutFilter::write_data_set(const std::string &name,
657 const unsigned int dimension,
658 const unsigned int set_num,
659 const Table<2, double> &data_vectors)
660 {
661 unsigned int new_dim;
662
663 // HDF5/XDMF output only supports 1d or 3d output, so force rearrangement if
664 // needed
665 if (flags.xdmf_hdf5_output && dimension != 1)
666 new_dim = 3;
667 else
668 new_dim = dimension;
669
670 // Record the data set name, dimension, and allocate space for it
671 data_set_names.push_back(name);
672 data_set_dims.push_back(new_dim);
673 data_sets.emplace_back(new_dim * existing_points.size());
674
675 // TODO: averaging, min/max, etc for merged vertices
676 for (unsigned int i = 0; i < filtered_points.size(); ++i)
677 {
678 const unsigned int r = filtered_points[i];
679
680 for (unsigned int d = 0; d < new_dim; ++d)
681 {
682 if (d < dimension)
683 data_sets.back()[r * new_dim + d] = data_vectors(set_num + d, i);
684 else
685 data_sets.back()[r * new_dim + d] = 0;
686 }
687 }
688 }
689} // namespace DataOutBase
690
691
692
693//----------------------------------------------------------------------//
694// Auxiliary data
695//----------------------------------------------------------------------//
696
697namespace
698{
699 const char *gmv_cell_type[4] = {"", "line 2", "quad 4", "hex 8"};
700
701 const char *ucd_cell_type[4] = {"pt", "line", "quad", "hex"};
702
703 const char *tecplot_cell_type[4] = {"", "lineseg", "quadrilateral", "brick"};
704
718 template <int dim, int spacedim>
719 std::array<unsigned int, 3>
720 extract_vtk_patch_info(const DataOutBase::Patch<dim, spacedim> &patch,
721 const bool write_higher_order_cells)
722 {
723 std::array<unsigned int, 3> vtk_cell_id = {
724 {/* cell type, tbd: */ numbers::invalid_unsigned_int,
725 /* # of cells, default: just one cell */ 1,
726 /* # of nodes, default: as many nodes as vertices */
727 patch.reference_cell.n_vertices()}};
728
729 if (write_higher_order_cells)
730 {
731 vtk_cell_id[0] = patch.reference_cell.vtk_lagrange_type();
732 vtk_cell_id[2] = patch.data.n_cols();
733 }
734 else if (patch.data.n_cols() == patch.reference_cell.n_vertices())
735 // One data set per vertex -> a linear cell
736 vtk_cell_id[0] = patch.reference_cell.vtk_linear_type();
737 else if (patch.reference_cell == ReferenceCells::Triangle &&
738 patch.data.n_cols() == 6)
739 {
741 vtk_cell_id[0] = patch.reference_cell.vtk_quadratic_type();
742 vtk_cell_id[2] = patch.data.n_cols();
743 }
745 patch.data.n_cols() == 10)
746 {
748 vtk_cell_id[0] = patch.reference_cell.vtk_quadratic_type();
749 vtk_cell_id[2] = patch.data.n_cols();
750 }
751 else if (patch.reference_cell.is_hyper_cube())
752 {
753 // For hypercubes, we support sub-divided linear cells
754 vtk_cell_id[0] = patch.reference_cell.vtk_linear_type();
755 vtk_cell_id[1] = Utilities::pow(patch.n_subdivisions, dim);
756 }
757 else if (patch.reference_cell.is_simplex())
758 {
759 vtk_cell_id[0] = patch.reference_cell.vtk_lagrange_type();
760 vtk_cell_id[2] = patch.data.n_cols();
761 }
762 else
763 {
765 }
766
767 return vtk_cell_id;
768 }
769
770 //----------------------------------------------------------------------//
771 // Auxiliary functions
772 //----------------------------------------------------------------------//
773
774 // For a given patch that corresponds to a hypercube cell, compute the
775 // location of a node interpolating the corner nodes linearly
776 // at the point lattice_location/n_subdivisions where lattice_location
777 // is a dim-dimensional integer vector. If the points are
778 // saved in the patch.data member, return the saved point instead.
779 template <int dim, int spacedim>
780 inline Point<spacedim>
781 get_equispaced_location(
783 const std::initializer_list<unsigned int> &lattice_location,
784 const unsigned int n_subdivisions)
785 {
786 // This function only makes sense when called on hypercube cells
788
789 Assert(lattice_location.size() == dim, ExcInternalError());
790
791 const unsigned int xstep = (dim > 0 ? *(lattice_location.begin() + 0) : 0);
792 const unsigned int ystep = (dim > 1 ? *(lattice_location.begin() + 1) : 0);
793 const unsigned int zstep = (dim > 2 ? *(lattice_location.begin() + 2) : 0);
794
795 // If the patch stores the locations of nodes (rather than of only the
796 // vertices), then obtain the location by direct lookup.
797 if (patch.points_are_available)
798 {
799 Assert(n_subdivisions == patch.n_subdivisions, ExcNotImplemented());
800
801 unsigned int point_no = 0;
802 switch (dim)
803 {
804 case 3:
805 AssertIndexRange(zstep, n_subdivisions + 1);
806 point_no += (n_subdivisions + 1) * (n_subdivisions + 1) * zstep;
808 case 2:
809 AssertIndexRange(ystep, n_subdivisions + 1);
810 point_no += (n_subdivisions + 1) * ystep;
812 case 1:
813 AssertIndexRange(xstep, n_subdivisions + 1);
814 point_no += xstep;
816 case 0:
817 // break here for dim<=3
818 break;
819
820 default:
822 }
823 Point<spacedim> node;
824 for (unsigned int d = 0; d < spacedim; ++d)
825 node[d] = patch.data(patch.data.size(0) - spacedim + d, point_no);
826 return node;
827 }
828 else
829 // The patch does not store node locations, so we have to interpolate
830 // between its vertices:
831 {
832 if (dim == 0)
833 return patch.vertices[0];
834 else
835 {
836 // perform a dim-linear interpolation
837 const double stepsize = 1. / n_subdivisions;
838 const double xfrac = xstep * stepsize;
839
840 Point<spacedim> node =
841 (patch.vertices[1] * xfrac) + (patch.vertices[0] * (1 - xfrac));
842 if (dim > 1)
843 {
844 const double yfrac = ystep * stepsize;
845 node *= 1 - yfrac;
846 node += ((patch.vertices[3] * xfrac) +
847 (patch.vertices[2] * (1 - xfrac))) *
848 yfrac;
849 if (dim > 2)
850 {
851 const double zfrac = zstep * stepsize;
852 node *= (1 - zfrac);
853 node += (((patch.vertices[5] * xfrac) +
854 (patch.vertices[4] * (1 - xfrac))) *
855 (1 - yfrac) +
856 ((patch.vertices[7] * xfrac) +
857 (patch.vertices[6] * (1 - xfrac))) *
858 yfrac) *
859 zfrac;
860 }
861 }
862 return node;
863 }
864 }
865 }
866
867 // For a given patch, compute the nodes for arbitrary (non-hypercube) cells.
868 // If the points are saved in the patch.data member, return the saved point
869 // instead.
870 template <int dim, int spacedim>
871 inline Point<spacedim>
872 get_node_location(const DataOutBase::Patch<dim, spacedim> &patch,
873 const unsigned int node_index)
874 {
875 // Due to a historical accident, we are using a different indexing
876 // for pyramids in this file than we do where we create patches.
877 // So translate if necessary.
878 unsigned int point_no_actual = node_index;
880 {
882
883 static const std::array<unsigned int, 5> table = {{0, 1, 3, 2, 4}};
884 point_no_actual = table[node_index];
885 }
886
887 // If the patch stores the locations of nodes (rather than of only the
888 // vertices), then obtain the location by direct lookup.
889 if (patch.points_are_available)
890 {
891 Point<spacedim> node;
892 for (unsigned int d = 0; d < spacedim; ++d)
893 node[d] =
894 patch.data(patch.data.size(0) - spacedim + d, point_no_actual);
895 return node;
896 }
897 else
898 // The patch does not store node locations, so we have to interpolate
899 // between its vertices. This isn't currently implemented for anything
900 // other than one subdivision, but would go here.
901 //
902 // For n_subdivisions==1, the locations are simply those of vertices, so
903 // get the information from there.
904 {
906
907 return patch.vertices[point_no_actual];
908 }
909 }
910
911
912
918 template <int dim, int spacedim>
919 std::tuple<unsigned int, unsigned int>
920 count_nodes_and_cells(
921 const std::vector<DataOutBase::Patch<dim, spacedim>> &patches)
922 {
923 unsigned int n_nodes = 0;
924 unsigned int n_cells = 0;
925 for (const auto &patch : patches)
926 {
929 "The reference cell for this patch is set to 'Invalid', "
930 "but that is clearly not a valid choice. Did you forget "
931 "to set the reference cell for the patch?"));
932
933 if (patch.reference_cell.is_hyper_cube())
934 {
935 n_nodes += Utilities::fixed_power<dim>(patch.n_subdivisions + 1);
936 n_cells += Utilities::fixed_power<dim>(patch.n_subdivisions);
937 }
938 else
939 {
941 n_nodes += patch.reference_cell.n_vertices();
942 n_cells += 1;
943 }
944 }
945
946 return std::make_tuple(n_nodes, n_cells);
947 }
948
949
950
956 template <int dim, int spacedim>
957 std::tuple<unsigned int, unsigned int, unsigned int>
958 count_nodes_and_cells_and_points(
959 const std::vector<DataOutBase::Patch<dim, spacedim>> &patches,
960 const bool write_higher_order_cells)
961 {
962 unsigned int n_nodes = 0;
963 unsigned int n_cells = 0;
964 unsigned int n_points_and_n_cells = 0;
965
966 for (const auto &patch : patches)
967 {
968 if (patch.reference_cell.is_hyper_cube())
969 {
970 n_nodes += Utilities::fixed_power<dim>(patch.n_subdivisions + 1);
971
972 if (write_higher_order_cells)
973 {
974 // Write all of these nodes as a single higher-order cell. So
975 // add one to the number of cells, and update the number of
976 // points appropriately.
977 n_cells += 1;
978 n_points_and_n_cells +=
979 1 + Utilities::fixed_power<dim>(patch.n_subdivisions + 1);
980 }
981 else
982 {
983 // Write all of these nodes as a collection of d-linear
984 // cells. Add the number of sub-cells to the total number of
985 // cells, and then add one for each cell plus the number of
986 // vertices per cell for each subcell to the number of points.
987 const unsigned int n_subcells =
988 Utilities::fixed_power<dim>(patch.n_subdivisions);
989 n_cells += n_subcells;
990 n_points_and_n_cells +=
991 n_subcells * (1 + GeometryInfo<dim>::vertices_per_cell);
992 }
993 }
994 else
995 {
996 n_nodes += patch.data.n_cols();
997 n_cells += 1;
998 n_points_and_n_cells += patch.data.n_cols() + 1;
999 }
1000 }
1001
1002 return std::make_tuple(n_nodes, n_cells, n_points_and_n_cells);
1003 }
1004
1010 template <typename FlagsType>
1011 class StreamBase
1012 {
1013 public:
1014 /*
1015 * Constructor. Stores a reference to the output stream for immediate use.
1016 */
1017 StreamBase(std::ostream &stream, const FlagsType &flags)
1018 : selected_component(numbers::invalid_unsigned_int)
1019 , stream(stream)
1020 , flags(flags)
1021 {}
1022
1027 template <int dim>
1028 void
1029 write_point(const unsigned int, const Point<dim> &)
1030 {
1031 Assert(false,
1032 ExcMessage("The derived class you are using needs to "
1033 "reimplement this function if you want to call "
1034 "it."));
1035 }
1036
1042 void
1043 flush_points()
1044 {}
1045
1051 template <int dim>
1052 void
1053 write_cell(const unsigned int /*index*/,
1054 const unsigned int /*start*/,
1055 std::array<unsigned int, dim> & /*offsets*/)
1056 {
1057 Assert(false,
1058 ExcMessage("The derived class you are using needs to "
1059 "reimplement this function if you want to call "
1060 "it."));
1061 }
1062
1069 void
1070 write_cell_single(const unsigned int index,
1071 const unsigned int start,
1072 const unsigned int n_points,
1073 const ReferenceCell &reference_cell)
1074 {
1075 (void)index;
1076 (void)start;
1077 (void)n_points;
1078 (void)reference_cell;
1079
1080 Assert(false,
1081 ExcMessage("The derived class you are using needs to "
1082 "reimplement this function if you want to call "
1083 "it."));
1084 }
1085
1092 void
1093 flush_cells()
1094 {}
1095
1100 template <typename T>
1101 std::ostream &
1102 operator<<(const T &t)
1103 {
1104 stream << t;
1105 return stream;
1106 }
1107
1114 unsigned int selected_component;
1115
1116 protected:
1121 std::ostream &stream;
1122
1126 const FlagsType flags;
1127 };
1128
1132 class DXStream : public StreamBase<DataOutBase::DXFlags>
1133 {
1134 public:
1135 DXStream(std::ostream &stream, const DataOutBase::DXFlags &flags);
1136
1137 template <int dim>
1138 void
1139 write_point(const unsigned int index, const Point<dim> &);
1140
1149 template <int dim>
1150 void
1151 write_cell(const unsigned int index,
1152 const unsigned int start,
1153 const std::array<unsigned int, dim> &offsets);
1154
1161 template <typename data>
1162 void
1163 write_dataset(const unsigned int index, const std::vector<data> &values);
1164 };
1165
1169 class GmvStream : public StreamBase<DataOutBase::GmvFlags>
1170 {
1171 public:
1172 GmvStream(std::ostream &stream, const DataOutBase::GmvFlags &flags);
1173
1174 template <int dim>
1175 void
1176 write_point(const unsigned int index, const Point<dim> &);
1177
1186 template <int dim>
1187 void
1188 write_cell(const unsigned int index,
1189 const unsigned int start,
1190 const std::array<unsigned int, dim> &offsets);
1191 };
1192
1196 class TecplotStream : public StreamBase<DataOutBase::TecplotFlags>
1197 {
1198 public:
1199 TecplotStream(std::ostream &stream, const DataOutBase::TecplotFlags &flags);
1200
1201 template <int dim>
1202 void
1203 write_point(const unsigned int index, const Point<dim> &);
1204
1213 template <int dim>
1214 void
1215 write_cell(const unsigned int index,
1216 const unsigned int start,
1217 const std::array<unsigned int, dim> &offsets);
1218 };
1219
1223 class UcdStream : public StreamBase<DataOutBase::UcdFlags>
1224 {
1225 public:
1226 UcdStream(std::ostream &stream, const DataOutBase::UcdFlags &flags);
1227
1228 template <int dim>
1229 void
1230 write_point(const unsigned int index, const Point<dim> &);
1231
1242 template <int dim>
1243 void
1244 write_cell(const unsigned int index,
1245 const unsigned int start,
1246 const std::array<unsigned int, dim> &offsets);
1247
1254 template <typename data>
1255 void
1256 write_dataset(const unsigned int index, const std::vector<data> &values);
1257 };
1258
1262 class VtkStream : public StreamBase<DataOutBase::VtkFlags>
1263 {
1264 public:
1265 VtkStream(std::ostream &stream, const DataOutBase::VtkFlags &flags);
1266
1267 template <int dim>
1268 void
1269 write_point(const unsigned int index, const Point<dim> &);
1270
1279 template <int dim>
1280 void
1281 write_cell(const unsigned int index,
1282 const unsigned int start,
1283 const std::array<unsigned int, dim> &offsets);
1284
1288 void
1289 write_cell_single(const unsigned int index,
1290 const unsigned int start,
1291 const unsigned int n_points,
1292 const ReferenceCell &reference_cell);
1293
1301 template <int dim>
1302 void
1303 write_high_order_cell(const unsigned int start,
1304 const std::vector<unsigned> &connectivity);
1305 };
1306
1307
1308 //----------------------------------------------------------------------//
1309
1310 DXStream::DXStream(std::ostream &out, const DataOutBase::DXFlags &f)
1311 : StreamBase<DataOutBase::DXFlags>(out, f)
1312 {}
1313
1314
1315 template <int dim>
1316 void
1317 DXStream::write_point(const unsigned int, const Point<dim> &p)
1318 {
1319 if (flags.coordinates_binary)
1320 {
1321 float data[dim];
1322 for (unsigned int d = 0; d < dim; ++d)
1323 data[d] = p[d];
1324 stream.write(reinterpret_cast<const char *>(data), dim * sizeof(*data));
1325 }
1326 else
1327 {
1328 for (unsigned int d = 0; d < dim; ++d)
1329 stream << p[d] << '\t';
1330 stream << '\n';
1331 }
1332 }
1333
1334
1335
1336 // Separate these out to avoid an internal compiler error with intel 17
1338 {
1343 std::array<unsigned int, GeometryInfo<0>::vertices_per_cell>
1344 set_node_numbers(const unsigned int /*start*/,
1345 const std::array<unsigned int, 0> & /*d1*/)
1346 {
1348 return {};
1349 }
1350
1351
1352
1353 std::array<unsigned int, GeometryInfo<1>::vertices_per_cell>
1354 set_node_numbers(const unsigned int start,
1355 const std::array<unsigned int, 1> &offsets)
1356 {
1357 std::array<unsigned int, GeometryInfo<1>::vertices_per_cell> nodes;
1358 nodes[0] = start;
1359 nodes[1] = start + offsets[0];
1360 return nodes;
1361 }
1362
1363
1364
1365 std::array<unsigned int, GeometryInfo<2>::vertices_per_cell>
1366 set_node_numbers(const unsigned int start,
1367 const std::array<unsigned int, 2> &offsets)
1368
1369 {
1370 const unsigned int d1 = offsets[0];
1371 const unsigned int d2 = offsets[1];
1372
1373 std::array<unsigned int, GeometryInfo<2>::vertices_per_cell> nodes;
1374 nodes[0] = start;
1375 nodes[1] = start + d1;
1376 nodes[2] = start + d2;
1377 nodes[3] = start + d2 + d1;
1378 return nodes;
1379 }
1380
1381
1382
1383 std::array<unsigned int, GeometryInfo<3>::vertices_per_cell>
1384 set_node_numbers(const unsigned int start,
1385 const std::array<unsigned int, 3> &offsets)
1386 {
1387 const unsigned int d1 = offsets[0];
1388 const unsigned int d2 = offsets[1];
1389 const unsigned int d3 = offsets[2];
1390
1391 std::array<unsigned int, GeometryInfo<3>::vertices_per_cell> nodes;
1392 nodes[0] = start;
1393 nodes[1] = start + d1;
1394 nodes[2] = start + d2;
1395 nodes[3] = start + d2 + d1;
1396 nodes[4] = start + d3;
1397 nodes[5] = start + d3 + d1;
1398 nodes[6] = start + d3 + d2;
1399 nodes[7] = start + d3 + d2 + d1;
1400 return nodes;
1401 }
1402 } // namespace DataOutBaseImplementation
1403
1404
1405
1406 template <int dim>
1407 void
1408 DXStream::write_cell(const unsigned int,
1409 const unsigned int start,
1410 const std::array<unsigned int, dim> &offsets)
1411 {
1412 const auto nodes =
1413 DataOutBaseImplementation::set_node_numbers(start, offsets);
1414
1415 if (flags.int_binary)
1416 {
1417 std::array<unsigned int, GeometryInfo<dim>::vertices_per_cell> temp;
1418 for (unsigned int i = 0; i < nodes.size(); ++i)
1419 temp[i] = nodes[GeometryInfo<dim>::dx_to_deal[i]];
1420 stream.write(reinterpret_cast<const char *>(temp.data()),
1421 temp.size() * sizeof(temp[0]));
1422 }
1423 else
1424 {
1425 for (unsigned int i = 0; i < nodes.size() - 1; ++i)
1426 stream << nodes[GeometryInfo<dim>::dx_to_deal[i]] << '\t';
1427 stream << nodes[GeometryInfo<dim>::dx_to_deal[nodes.size() - 1]]
1428 << '\n';
1429 }
1430 }
1431
1432
1433
1434 template <typename data>
1435 inline void
1436 DXStream::write_dataset(const unsigned int, const std::vector<data> &values)
1437 {
1438 if (flags.data_binary)
1439 {
1440 stream.write(reinterpret_cast<const char *>(values.data()),
1441 values.size() * sizeof(data));
1442 }
1443 else
1444 {
1445 for (unsigned int i = 0; i < values.size(); ++i)
1446 stream << '\t' << values[i];
1447 stream << '\n';
1448 }
1449 }
1450
1451
1452
1453 //----------------------------------------------------------------------//
1454
1455 GmvStream::GmvStream(std::ostream &out, const DataOutBase::GmvFlags &f)
1456 : StreamBase<DataOutBase::GmvFlags>(out, f)
1457 {}
1458
1459
1460 template <int dim>
1461 void
1462 GmvStream::write_point(const unsigned int, const Point<dim> &p)
1463 {
1464 Assert(selected_component != numbers::invalid_unsigned_int,
1466 stream << p[selected_component] << ' ';
1467 }
1468
1469
1470
1471 template <int dim>
1472 void
1473 GmvStream::write_cell(const unsigned int,
1474 const unsigned int s,
1475 const std::array<unsigned int, dim> &offsets)
1476 {
1477 // Vertices are numbered starting with one.
1478 const unsigned int start = s + 1;
1479 stream << gmv_cell_type[dim] << '\n';
1480
1481 switch (dim)
1482 {
1483 case 0:
1484 {
1485 stream << start;
1486 break;
1487 }
1488
1489 case 1:
1490 {
1491 const unsigned int d1 = offsets[0];
1492 stream << start;
1493 stream << '\t' << start + d1;
1494 break;
1495 }
1496
1497 case 2:
1498 {
1499 const unsigned int d1 = offsets[0];
1500 const unsigned int d2 = offsets[1];
1501 stream << start;
1502 stream << '\t' << start + d1;
1503 stream << '\t' << start + d2 + d1 << '\t' << start + d2;
1504 break;
1505 }
1506
1507 case 3:
1508 {
1509 const unsigned int d1 = offsets[0];
1510 const unsigned int d2 = offsets[1];
1511 const unsigned int d3 = offsets[2];
1512 stream << start;
1513 stream << '\t' << start + d1;
1514 stream << '\t' << start + d2 + d1 << '\t' << start + d2;
1515 stream << '\t' << start + d3 << '\t' << start + d3 + d1 << '\t'
1516 << start + d3 + d2 + d1 << '\t' << start + d3 + d2;
1517 break;
1518 }
1519
1520 default:
1522 }
1523 stream << '\n';
1524 }
1525
1526
1527
1528 TecplotStream::TecplotStream(std::ostream &out,
1530 : StreamBase<DataOutBase::TecplotFlags>(out, f)
1531 {}
1532
1533
1534 template <int dim>
1535 void
1536 TecplotStream::write_point(const unsigned int, const Point<dim> &p)
1537 {
1538 Assert(selected_component != numbers::invalid_unsigned_int,
1540 stream << p[selected_component] << '\n';
1541 }
1542
1543
1544
1545 template <int dim>
1546 void
1547 TecplotStream::write_cell(const unsigned int,
1548 const unsigned int s,
1549 const std::array<unsigned int, dim> &offsets)
1550 {
1551 const unsigned int start = s + 1;
1552
1553 switch (dim)
1554 {
1555 case 0:
1556 {
1557 stream << start;
1558 break;
1559 }
1560
1561 case 1:
1562 {
1563 const unsigned int d1 = offsets[0];
1564 stream << start;
1565 stream << '\t' << start + d1;
1566 break;
1567 }
1568
1569 case 2:
1570 {
1571 const unsigned int d1 = offsets[0];
1572 const unsigned int d2 = offsets[1];
1573 stream << start;
1574 stream << '\t' << start + d1;
1575 stream << '\t' << start + d2 + d1 << '\t' << start + d2;
1576 break;
1577 }
1578
1579 case 3:
1580 {
1581 const unsigned int d1 = offsets[0];
1582 const unsigned int d2 = offsets[1];
1583 const unsigned int d3 = offsets[2];
1584 stream << start;
1585 stream << '\t' << start + d1;
1586 stream << '\t' << start + d2 + d1 << '\t' << start + d2;
1587 stream << '\t' << start + d3 << '\t' << start + d3 + d1 << '\t'
1588 << start + d3 + d2 + d1 << '\t' << start + d3 + d2;
1589 break;
1590 }
1591
1592 default:
1594 }
1595 stream << '\n';
1596 }
1597
1598
1599
1600 UcdStream::UcdStream(std::ostream &out, const DataOutBase::UcdFlags &f)
1601 : StreamBase<DataOutBase::UcdFlags>(out, f)
1602 {}
1603
1604
1605 template <int dim>
1606 void
1607 UcdStream::write_point(const unsigned int index, const Point<dim> &p)
1608 {
1609 stream << index + 1 << " ";
1610 // write out coordinates
1611 for (unsigned int i = 0; i < dim; ++i)
1612 stream << p[i] << ' ';
1613 // fill with zeroes
1614 for (unsigned int i = dim; i < 3; ++i)
1615 stream << "0 ";
1616 stream << '\n';
1617 }
1618
1619
1620
1621 template <int dim>
1622 void
1623 UcdStream::write_cell(const unsigned int index,
1624 const unsigned int start,
1625 const std::array<unsigned int, dim> &offsets)
1626 {
1627 const auto nodes =
1628 DataOutBaseImplementation::set_node_numbers(start, offsets);
1629
1630 // Write out all cells and remember that all indices must be shifted by one.
1631 stream << index + 1 << "\t0 " << ucd_cell_type[dim];
1632 for (unsigned int i = 0; i < nodes.size(); ++i)
1633 stream << '\t' << nodes[GeometryInfo<dim>::ucd_to_deal[i]] + 1;
1634 stream << '\n';
1635 }
1636
1637
1638
1639 template <typename data>
1640 inline void
1641 UcdStream::write_dataset(const unsigned int index,
1642 const std::vector<data> &values)
1643 {
1644 stream << index + 1;
1645 for (unsigned int i = 0; i < values.size(); ++i)
1646 stream << '\t' << values[i];
1647 stream << '\n';
1648 }
1649
1650
1651
1652 //----------------------------------------------------------------------//
1653
1654 VtkStream::VtkStream(std::ostream &out, const DataOutBase::VtkFlags &f)
1655 : StreamBase<DataOutBase::VtkFlags>(out, f)
1656 {}
1657
1658
1659 template <int dim>
1660 void
1661 VtkStream::write_point(const unsigned int, const Point<dim> &p)
1662 {
1663 // write out coordinates
1664 stream << p;
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 VtkStream::write_cell(const unsigned int,
1676 const unsigned int start,
1677 const std::array<unsigned int, dim> &offsets)
1678 {
1679 stream << GeometryInfo<dim>::vertices_per_cell << '\t';
1680
1681 switch (dim)
1682 {
1683 case 0:
1684 {
1685 stream << start;
1686 break;
1687 }
1688
1689 case 1:
1690 {
1691 const unsigned int d1 = offsets[0];
1692 stream << start;
1693 stream << '\t' << start + d1;
1694 break;
1695 }
1696
1697 case 2:
1698 {
1699 const unsigned int d1 = offsets[0];
1700 const unsigned int d2 = offsets[1];
1701 stream << start;
1702 stream << '\t' << start + d1;
1703 stream << '\t' << start + d2 + d1 << '\t' << start + d2;
1704 break;
1705 }
1706
1707 case 3:
1708 {
1709 const unsigned int d1 = offsets[0];
1710 const unsigned int d2 = offsets[1];
1711 const unsigned int d3 = offsets[2];
1712 stream << start;
1713 stream << '\t' << start + d1;
1714 stream << '\t' << start + d2 + d1 << '\t' << start + d2;
1715 stream << '\t' << start + d3 << '\t' << start + d3 + d1 << '\t'
1716 << start + d3 + d2 + d1 << '\t' << start + d3 + d2;
1717 break;
1718 }
1719
1720 default:
1722 }
1723 stream << '\n';
1724 }
1725
1726
1727
1728 void
1729 VtkStream::write_cell_single(const unsigned int index,
1730 const unsigned int start,
1731 const unsigned int n_points,
1732 const ReferenceCell &reference_cell)
1733 {
1734 (void)index;
1735
1736 static const std::array<unsigned int, 5> table = {{0, 1, 3, 2, 4}};
1737
1738 stream << '\t' << n_points;
1739 for (unsigned int i = 0; i < n_points; ++i)
1740 stream << '\t'
1741 << start +
1742 (reference_cell == ReferenceCells::Pyramid ? table[i] : i);
1743 stream << '\n';
1744 }
1745
1746 template <int dim>
1747 void
1748 VtkStream::write_high_order_cell(const unsigned int start,
1749 const std::vector<unsigned> &connectivity)
1750 {
1751 stream << connectivity.size();
1752 for (const auto &c : connectivity)
1753 stream << '\t' << start + c;
1754 stream << '\n';
1755 }
1756} // namespace
1757
1758
1759
1760namespace DataOutBase
1761{
1762 const unsigned int Deal_II_IntermediateFlags::format_version = 4;
1763
1764
1765 template <int dim, int spacedim>
1766 const unsigned int Patch<dim, spacedim>::space_dim;
1767
1768
1769 template <int dim, int spacedim>
1770 const unsigned int Patch<dim, spacedim>::no_neighbor;
1771
1772
1773 template <int dim, int spacedim>
1775 : patch_index(no_neighbor)
1776 , n_subdivisions(1)
1777 , points_are_available(false)
1778 , reference_cell(ReferenceCells::Invalid)
1779 // all the other data has a constructor of its own, except for the "neighbors"
1780 // field, which we set to invalid values.
1781 {
1782 for (const unsigned int i : GeometryInfo<dim>::face_indices())
1784
1785 AssertIndexRange(dim, spacedim + 1);
1786 Assert(spacedim <= 3, ExcNotImplemented());
1787 }
1788
1789
1790
1791 template <int dim, int spacedim>
1792 bool
1794 {
1795 if (reference_cell != patch.reference_cell)
1796 return false;
1797
1798 // TODO: make tolerance relative
1799 const double epsilon = 3e-16;
1800 for (const unsigned int i : GeometryInfo<dim>::vertex_indices())
1801 if (vertices[i].distance(patch.vertices[i]) > epsilon)
1802 return false;
1803
1804 for (const unsigned int i : GeometryInfo<dim>::face_indices())
1805 if (neighbors[i] != patch.neighbors[i])
1806 return false;
1807
1808 if (patch_index != patch.patch_index)
1809 return false;
1810
1811 if (n_subdivisions != patch.n_subdivisions)
1812 return false;
1813
1814 if (points_are_available != patch.points_are_available)
1815 return false;
1816
1817 if (data.n_rows() != patch.data.n_rows())
1818 return false;
1819
1820 if (data.n_cols() != patch.data.n_cols())
1821 return false;
1822
1823 for (unsigned int i = 0; i < data.n_rows(); ++i)
1824 for (unsigned int j = 0; j < data.n_cols(); ++j)
1825 if (data[i][j] != patch.data[i][j])
1826 return false;
1827
1828 return true;
1829 }
1830
1831
1832
1833 template <int dim, int spacedim>
1834 std::size_t
1836 {
1837 return (sizeof(vertices) / sizeof(vertices[0]) *
1839 sizeof(neighbors) / sizeof(neighbors[0]) *
1844 MemoryConsumption::memory_consumption(points_are_available) +
1845 sizeof(reference_cell));
1846 }
1847
1848
1849
1850 template <int dim, int spacedim>
1851 void
1853 {
1854 std::swap(vertices, other_patch.vertices);
1855 std::swap(neighbors, other_patch.neighbors);
1856 std::swap(patch_index, other_patch.patch_index);
1857 std::swap(n_subdivisions, other_patch.n_subdivisions);
1858 data.swap(other_patch.data);
1859 std::swap(points_are_available, other_patch.points_are_available);
1860 std::swap(reference_cell, other_patch.reference_cell);
1861 }
1862
1863
1864
1865 template <int spacedim>
1866 const unsigned int Patch<0, spacedim>::space_dim;
1867
1868
1869 template <int spacedim>
1870 const unsigned int Patch<0, spacedim>::no_neighbor;
1871
1872
1873 template <int spacedim>
1874 unsigned int Patch<0, spacedim>::neighbors[1] = {
1876
1877 template <int spacedim>
1878 const unsigned int Patch<0, spacedim>::n_subdivisions = 1;
1879
1880 template <int spacedim>
1883
1884 template <int spacedim>
1886 : patch_index(no_neighbor)
1887 , points_are_available(false)
1888 {
1889 Assert(spacedim <= 3, ExcNotImplemented());
1890 }
1891
1892
1893
1894 template <int spacedim>
1895 bool
1897 {
1898 const unsigned int dim = 0;
1899
1900 // TODO: make tolerance relative
1901 const double epsilon = 3e-16;
1902 for (const unsigned int i : GeometryInfo<dim>::vertex_indices())
1903 if (vertices[i].distance(patch.vertices[i]) > epsilon)
1904 return false;
1905
1906 if (patch_index != patch.patch_index)
1907 return false;
1908
1909 if (points_are_available != patch.points_are_available)
1910 return false;
1911
1912 if (data.n_rows() != patch.data.n_rows())
1913 return false;
1914
1915 if (data.n_cols() != patch.data.n_cols())
1916 return false;
1917
1918 for (unsigned int i = 0; i < data.n_rows(); ++i)
1919 for (unsigned int j = 0; j < data.n_cols(); ++j)
1920 if (data[i][j] != patch.data[i][j])
1921 return false;
1922
1923 return true;
1924 }
1925
1926
1927
1928 template <int spacedim>
1929 std::size_t
1931 {
1932 return (sizeof(vertices) / sizeof(vertices[0]) *
1935 MemoryConsumption::memory_consumption(points_are_available));
1936 }
1937
1938
1939
1940 template <int spacedim>
1941 void
1943 {
1944 std::swap(vertices, other_patch.vertices);
1945 std::swap(patch_index, other_patch.patch_index);
1946 data.swap(other_patch.data);
1947 std::swap(points_are_available, other_patch.points_are_available);
1948 }
1949
1950
1951
1952 UcdFlags::UcdFlags(const bool write_preamble)
1953 : write_preamble(write_preamble)
1954 {}
1955
1956
1957
1959 {
1960 space_dimension_labels.emplace_back("x");
1961 space_dimension_labels.emplace_back("y");
1962 space_dimension_labels.emplace_back("z");
1963 }
1964
1965
1966
1967 GnuplotFlags::GnuplotFlags(const std::vector<std::string> &labels)
1968 : space_dimension_labels(labels)
1969 {}
1970
1971
1972
1973 std::size_t
1978
1979
1980
1981 PovrayFlags::PovrayFlags(const bool smooth,
1982 const bool bicubic_patch,
1983 const bool external_data)
1984 : smooth(smooth)
1985 , bicubic_patch(bicubic_patch)
1986 , external_data(external_data)
1987 {}
1988
1989
1990 DataOutFilterFlags::DataOutFilterFlags(const bool filter_duplicate_vertices,
1991 const bool xdmf_hdf5_output)
1992 : filter_duplicate_vertices(filter_duplicate_vertices)
1993 , xdmf_hdf5_output(xdmf_hdf5_output)
1994 {}
1995
1996
1997 void
1999 {
2000 prm.declare_entry(
2001 "Filter duplicate vertices",
2002 "false",
2004 "Whether to remove duplicate vertex values. deal.II duplicates "
2005 "vertices once for each adjacent cell so that it can output "
2006 "discontinuous quantities for which there may be more than one "
2007 "value for each vertex position. Setting this flag to "
2008 "'true' will merge all of these values by selecting a "
2009 "random one and outputting this as 'the' value for the vertex. "
2010 "As long as the data to be output corresponds to continuous "
2011 "fields, merging vertices has no effect. On the other hand, "
2012 "if the data to be output corresponds to discontinuous fields "
2013 "(either because you are using a discontinuous finite element, "
2014 "or because you are using a DataPostprocessor that yields "
2015 "discontinuous data, or because the data to be output has been "
2016 "produced by entirely different means), then the data in the "
2017 "output file no longer faithfully represents the underlying data "
2018 "because the discontinuous field has been replaced by a "
2019 "continuous one. Note also that the filtering can not occur "
2020 "on processor boundaries. Thus, a filtered discontinuous field "
2021 "looks like a continuous field inside of a subdomain, "
2022 "but like a discontinuous field at the subdomain boundary."
2023 "\n\n"
2024 "In any case, filtering results in drastically smaller output "
2025 "files (smaller by about a factor of 2^dim).");
2026 prm.declare_entry(
2027 "XDMF HDF5 output",
2028 "false",
2030 "Whether the data will be used in an XDMF/HDF5 combination.");
2031 }
2032
2033
2034
2035 void
2037 {
2038 filter_duplicate_vertices = prm.get_bool("Filter duplicate vertices");
2039 xdmf_hdf5_output = prm.get_bool("XDMF HDF5 output");
2040 }
2041
2042
2043
2044 DXFlags::DXFlags(const bool write_neighbors,
2045 const bool int_binary,
2046 const bool coordinates_binary,
2047 const bool data_binary)
2048 : write_neighbors(write_neighbors)
2049 , int_binary(int_binary)
2050 , coordinates_binary(coordinates_binary)
2051 , data_binary(data_binary)
2052 , data_double(false)
2053 {}
2054
2055
2056 void
2058 {
2059 prm.declare_entry("Write neighbors",
2060 "true",
2062 "A boolean field indicating whether neighborship "
2063 "information between cells is to be written to the "
2064 "OpenDX output file");
2065 prm.declare_entry("Integer format",
2066 "ascii",
2067 Patterns::Selection("ascii|32|64"),
2068 "Output format of integer numbers, which is "
2069 "either a text representation (ascii) or binary integer "
2070 "values of 32 or 64 bits length");
2071 prm.declare_entry("Coordinates format",
2072 "ascii",
2073 Patterns::Selection("ascii|32|64"),
2074 "Output format of vertex coordinates, which is "
2075 "either a text representation (ascii) or binary "
2076 "floating point values of 32 or 64 bits length");
2077 prm.declare_entry("Data format",
2078 "ascii",
2079 Patterns::Selection("ascii|32|64"),
2080 "Output format of data values, which is "
2081 "either a text representation (ascii) or binary "
2082 "floating point values of 32 or 64 bits length");
2083 }
2084
2085
2086
2087 void
2089 {
2090 write_neighbors = prm.get_bool("Write neighbors");
2091 // TODO:[GK] Read the new parameters
2092 }
2093
2094
2095
2096 void
2098 {
2099 prm.declare_entry("Write preamble",
2100 "true",
2102 "A flag indicating whether a comment should be "
2103 "written to the beginning of the output file "
2104 "indicating date and time of creation as well "
2105 "as the creating program");
2106 }
2107
2108
2109
2110 void
2112 {
2113 write_preamble = prm.get_bool("Write preamble");
2114 }
2115
2116
2117
2118 SvgFlags::SvgFlags(const unsigned int height_vector,
2119 const int azimuth_angle,
2120 const int polar_angle,
2121 const unsigned int line_thickness,
2122 const bool margin,
2123 const bool draw_colorbar)
2124 : height(4000)
2125 , width(0)
2126 , height_vector(height_vector)
2127 , azimuth_angle(azimuth_angle)
2128 , polar_angle(polar_angle)
2129 , line_thickness(line_thickness)
2130 , margin(margin)
2131 , draw_colorbar(draw_colorbar)
2132 {}
2133
2134
2135
2136 void
2138 {
2139 prm.declare_entry("Use smooth triangles",
2140 "false",
2142 "A flag indicating whether POVRAY should use smoothed "
2143 "triangles instead of the usual ones");
2144 prm.declare_entry("Use bicubic patches",
2145 "false",
2147 "Whether POVRAY should use bicubic patches");
2148 prm.declare_entry("Include external file",
2149 "true",
2151 "Whether camera and lighting information should "
2152 "be put into an external file \"data.inc\" or into "
2153 "the POVRAY input file");
2154 }
2155
2156
2157
2158 void
2160 {
2161 smooth = prm.get_bool("Use smooth triangles");
2162 bicubic_patch = prm.get_bool("Use bicubic patches");
2163 external_data = prm.get_bool("Include external file");
2164 }
2165
2166
2167
2168 EpsFlags::EpsFlags(const unsigned int height_vector,
2169 const unsigned int color_vector,
2170 const SizeType size_type,
2171 const unsigned int size,
2172 const double line_width,
2173 const double azimut_angle,
2174 const double turn_angle,
2175 const double z_scaling,
2176 const bool draw_mesh,
2177 const bool draw_cells,
2178 const bool shade_cells,
2179 const ColorFunction color_function)
2180 : height_vector(height_vector)
2181 , color_vector(color_vector)
2182 , size_type(size_type)
2183 , size(size)
2184 , line_width(line_width)
2185 , azimut_angle(azimut_angle)
2186 , turn_angle(turn_angle)
2187 , z_scaling(z_scaling)
2188 , draw_mesh(draw_mesh)
2189 , draw_cells(draw_cells)
2190 , shade_cells(shade_cells)
2191 , color_function(color_function)
2192 {}
2193
2194
2195
2198 const double xmin,
2199 const double xmax)
2200 {
2201 RgbValues rgb_values = {0, 0, 0};
2202
2203 // A difficult color scale:
2204 // xmin = black [1]
2205 // 3/4*xmin+1/4*xmax = blue [2]
2206 // 1/2*xmin+1/2*xmax = green (3)
2207 // 1/4*xmin+3/4*xmax = red (4)
2208 // xmax = white (5)
2209 // Makes the following color functions:
2210 //
2211 // red green blue
2212 // __
2213 // / /\ / /\ /
2214 // ____/ __/ \/ / \__/
2215
2216 // { 0 [1] - (3)
2217 // r = { ( 4*x-2*xmin+2*xmax)/(xmax-xmin) (3) - (4)
2218 // { 1 (4) - (5)
2219 //
2220 // { 0 [1] - [2]
2221 // g = { ( 4*x-3*xmin- xmax)/(xmax-xmin) [2] - (3)
2222 // { (-4*x+ xmin+3*xmax)/(xmax-xmin) (3) - (4)
2223 // { ( 4*x- xmin-3*xmax)/(xmax-xmin) (4) - (5)
2224 //
2225 // { ( 4*x-4*xmin )/(xmax-xmin) [1] - [2]
2226 // b = { (-4*x+2*xmin+2*xmax)/(xmax-xmin) [2] - (3)
2227 // { 0 (3) - (4)
2228 // { ( 4*x- xmin-3*xmax)/(xmax-xmin) (4) - (5)
2229
2230 double sum = xmax + xmin;
2231 double sum13 = xmin + 3 * xmax;
2232 double sum22 = 2 * xmin + 2 * xmax;
2233 double sum31 = 3 * xmin + xmax;
2234 double dif = xmax - xmin;
2235 double rezdif = 1.0 / dif;
2236
2237 int where;
2238
2239 if (x < (sum31) / 4)
2240 where = 0;
2241 else if (x < (sum22) / 4)
2242 where = 1;
2243 else if (x < (sum13) / 4)
2244 where = 2;
2245 else
2246 where = 3;
2247
2248 if (dif != 0)
2249 {
2250 switch (where)
2251 {
2252 case 0:
2253 rgb_values.red = 0;
2254 rgb_values.green = 0;
2255 rgb_values.blue = (x - xmin) * 4. * rezdif;
2256 break;
2257 case 1:
2258 rgb_values.red = 0;
2259 rgb_values.green = (4 * x - 3 * xmin - xmax) * rezdif;
2260 rgb_values.blue = (sum22 - 4. * x) * rezdif;
2261 break;
2262 case 2:
2263 rgb_values.red = (4 * x - 2 * sum) * rezdif;
2264 rgb_values.green = (xmin + 3 * xmax - 4 * x) * rezdif;
2265 rgb_values.blue = 0;
2266 break;
2267 case 3:
2268 rgb_values.red = 1;
2269 rgb_values.green = (4 * x - xmin - 3 * xmax) * rezdif;
2270 rgb_values.blue = (4. * x - sum13) * rezdif;
2271 break;
2272 default:
2273 break;
2274 }
2275 }
2276 else // White
2277 rgb_values.red = rgb_values.green = rgb_values.blue = 1;
2278
2279 return rgb_values;
2280 }
2281
2282
2283
2286 const double xmin,
2287 const double xmax)
2288 {
2289 EpsFlags::RgbValues rgb_values;
2290 rgb_values.red = rgb_values.blue = rgb_values.green =
2291 (x - xmin) / (xmax - xmin);
2292 return rgb_values;
2293 }
2294
2295
2296
2299 const double xmin,
2300 const double xmax)
2301 {
2302 EpsFlags::RgbValues rgb_values;
2303 rgb_values.red = rgb_values.blue = rgb_values.green =
2304 1 - (x - xmin) / (xmax - xmin);
2305 return rgb_values;
2306 }
2307
2308
2309
2310 void
2312 {
2313 prm.declare_entry("Index of vector for height",
2314 "0",
2316 "Number of the input vector that is to be used to "
2317 "generate height information");
2318 prm.declare_entry("Index of vector for color",
2319 "0",
2321 "Number of the input vector that is to be used to "
2322 "generate color information");
2323 prm.declare_entry("Scale to width or height",
2324 "width",
2325 Patterns::Selection("width|height"),
2326 "Whether width or height should be scaled to match "
2327 "the given size");
2328 prm.declare_entry("Size (width or height) in eps units",
2329 "300",
2331 "The size (width or height) to which the eps output "
2332 "file is to be scaled");
2333 prm.declare_entry("Line widths in eps units",
2334 "0.5",
2336 "The width in which the postscript renderer is to "
2337 "plot lines");
2338 prm.declare_entry("Azimut angle",
2339 "60",
2340 Patterns::Double(0, 180),
2341 "Angle of the viewing position against the vertical "
2342 "axis");
2343 prm.declare_entry("Turn angle",
2344 "30",
2345 Patterns::Double(0, 360),
2346 "Angle of the viewing direction against the y-axis");
2347 prm.declare_entry("Scaling for z-axis",
2348 "1",
2350 "Scaling for the z-direction relative to the scaling "
2351 "used in x- and y-directions");
2352 prm.declare_entry("Draw mesh lines",
2353 "true",
2355 "Whether the mesh lines, or only the surface should be "
2356 "drawn");
2357 prm.declare_entry("Fill interior of cells",
2358 "true",
2360 "Whether only the mesh lines, or also the interior of "
2361 "cells should be plotted. If this flag is false, then "
2362 "one can see through the mesh");
2363 prm.declare_entry("Color shading of interior of cells",
2364 "true",
2366 "Whether the interior of cells shall be shaded");
2367 prm.declare_entry("Color function",
2368 "default",
2370 "default|grey scale|reverse grey scale"),
2371 "Name of a color function used to colorize mesh lines "
2372 "and/or cell interiors");
2373 }
2374
2375
2376
2377 void
2379 {
2380 height_vector = prm.get_integer("Index of vector for height");
2381 color_vector = prm.get_integer("Index of vector for color");
2382 if (prm.get("Scale to width or height") == "width")
2383 size_type = width;
2384 else
2385 size_type = height;
2386 size = prm.get_integer("Size (width or height) in eps units");
2387 line_width = prm.get_double("Line widths in eps units");
2388 azimut_angle = prm.get_double("Azimut angle");
2389 turn_angle = prm.get_double("Turn angle");
2390 z_scaling = prm.get_double("Scaling for z-axis");
2391 draw_mesh = prm.get_bool("Draw mesh lines");
2392 draw_cells = prm.get_bool("Fill interior of cells");
2393 shade_cells = prm.get_bool("Color shading of interior of cells");
2394 if (prm.get("Color function") == "default")
2396 else if (prm.get("Color function") == "grey scale")
2398 else if (prm.get("Color function") == "reverse grey scale")
2400 else
2401 // we shouldn't get here, since the parameter object should already have
2402 // checked that the given value is valid
2404 }
2405
2406
2408 : compression_level(compression_level)
2409 {}
2410
2411
2412 TecplotFlags::TecplotFlags(const char *zone_name, const double solution_time)
2413 : zone_name(zone_name)
2414 , solution_time(solution_time)
2415 {}
2416
2417
2418
2419 std::size_t
2421 {
2422 return sizeof(*this) + MemoryConsumption::memory_consumption(zone_name);
2423 }
2424
2425
2426
2427 VtkFlags::VtkFlags(const double time,
2428 const unsigned int cycle,
2429 const bool print_date_and_time,
2430 const CompressionLevel compression_level,
2431 const bool write_higher_order_cells,
2432 const std::map<std::string, std::string> &physical_units)
2433 : time(time)
2434 , cycle(cycle)
2435 , print_date_and_time(print_date_and_time)
2436 , compression_level(compression_level)
2437 , write_higher_order_cells(write_higher_order_cells)
2438 , physical_units(physical_units)
2439 {}
2440
2441
2442
2444 parse_output_format(const std::string &format_name)
2445 {
2446 if (format_name == "none")
2447 return none;
2448
2449 if (format_name == "dx")
2450 return dx;
2451
2452 if (format_name == "ucd")
2453 return ucd;
2454
2455 if (format_name == "gnuplot")
2456 return gnuplot;
2457
2458 if (format_name == "povray")
2459 return povray;
2460
2461 if (format_name == "eps")
2462 return eps;
2463
2464 if (format_name == "gmv")
2465 return gmv;
2466
2467 if (format_name == "tecplot")
2468 return tecplot;
2469
2470 if (format_name == "vtk")
2471 return vtk;
2472
2473 if (format_name == "vtu")
2474 return vtu;
2475
2476 if (format_name == "deal.II intermediate")
2477 return deal_II_intermediate;
2478
2479 if (format_name == "hdf5")
2480 return hdf5;
2481
2482 AssertThrow(false,
2483 ExcMessage("The given file format name is not recognized: <" +
2484 format_name + ">"));
2485
2486 // return something invalid
2487 return OutputFormat(-1);
2488 }
2489
2490
2491
2492 std::string
2494 {
2495 return "none|dx|ucd|gnuplot|povray|eps|gmv|tecplot|vtk|vtu|hdf5|svg|deal.II intermediate";
2496 }
2497
2498
2499
2500 std::string
2501 default_suffix(const OutputFormat output_format)
2502 {
2503 switch (output_format)
2504 {
2505 case none:
2506 return "";
2507 case dx:
2508 return ".dx";
2509 case ucd:
2510 return ".inp";
2511 case gnuplot:
2512 return ".gnuplot";
2513 case povray:
2514 return ".pov";
2515 case eps:
2516 return ".eps";
2517 case gmv:
2518 return ".gmv";
2519 case tecplot:
2520 return ".dat";
2521 case vtk:
2522 return ".vtk";
2523 case vtu:
2524 return ".vtu";
2526 return ".d2";
2527 case hdf5:
2528 return ".h5";
2529 case svg:
2530 return ".svg";
2531 default:
2533 return "";
2534 }
2535 }
2536
2537
2538 //----------------------------------------------------------------------//
2539
2540
2545 template <int dim, int spacedim>
2546 std::vector<Point<spacedim>>
2547 get_node_positions(const std::vector<Patch<dim, spacedim>> &patches)
2548 {
2549 Assert(dim <= 3, ExcNotImplemented());
2550 static const std::array<unsigned int, 5> table = {{0, 1, 3, 2, 4}};
2551
2552 std::vector<Point<spacedim>> node_positions;
2553 for (const auto &patch : patches)
2554 {
2555 // special treatment of non-hypercube cells
2556 if (patch.reference_cell != ReferenceCells::get_hypercube<dim>())
2557 {
2558 for (unsigned int point_no = 0; point_no < patch.data.n_cols();
2559 ++point_no)
2560 node_positions.emplace_back(get_node_location(
2561 patch,
2563 table[point_no] :
2564 point_no)));
2565 }
2566 else
2567 {
2568 const unsigned int n_subdivisions = patch.n_subdivisions;
2569 const unsigned int n = n_subdivisions + 1;
2570
2571 switch (dim)
2572 {
2573 case 0:
2574 node_positions.emplace_back(
2575 get_equispaced_location(patch, {}, n_subdivisions));
2576 break;
2577 case 1:
2578 for (unsigned int i1 = 0; i1 < n; ++i1)
2579 node_positions.emplace_back(
2580 get_equispaced_location(patch, {i1}, n_subdivisions));
2581 break;
2582 case 2:
2583 for (unsigned int i2 = 0; i2 < n; ++i2)
2584 for (unsigned int i1 = 0; i1 < n; ++i1)
2585 node_positions.emplace_back(get_equispaced_location(
2586 patch, {i1, i2}, n_subdivisions));
2587 break;
2588 case 3:
2589 for (unsigned int i3 = 0; i3 < n; ++i3)
2590 for (unsigned int i2 = 0; i2 < n; ++i2)
2591 for (unsigned int i1 = 0; i1 < n; ++i1)
2592 node_positions.emplace_back(get_equispaced_location(
2593 patch, {i1, i2, i3}, n_subdivisions));
2594 break;
2595
2596 default:
2598 }
2599 }
2600 }
2601
2602 return node_positions;
2603 }
2604
2605
2606 template <int dim, int spacedim, typename StreamType>
2607 void
2608 write_nodes(const std::vector<Patch<dim, spacedim>> &patches, StreamType &out)
2609 {
2610 // Obtain the node locations, and then output them via the given stream
2611 // object
2612 const std::vector<Point<spacedim>> node_positions =
2613 get_node_positions(patches);
2614
2615 int count = 0;
2616 for (const auto &node : node_positions)
2617 out.write_point(count++, node);
2618 out.flush_points();
2619 }
2620
2621
2622
2623 template <int dim, int spacedim, typename StreamType>
2624 void
2625 write_cells(const std::vector<Patch<dim, spacedim>> &patches, StreamType &out)
2626 {
2627 Assert(dim <= 3, ExcNotImplemented());
2628 unsigned int count = 0;
2629 unsigned int first_vertex_of_patch = 0;
2630 for (const auto &patch : patches)
2631 {
2632 // special treatment of simplices since they are not subdivided
2633 if (patch.reference_cell != ReferenceCells::get_hypercube<dim>())
2634 {
2635 out.write_cell_single(count++,
2636 first_vertex_of_patch,
2637 patch.data.n_cols(),
2638 patch.reference_cell);
2639 first_vertex_of_patch += patch.data.n_cols();
2640 }
2641 else // hypercube cell
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 {
2650 const unsigned int offset = first_vertex_of_patch;
2651 out.template write_cell<0>(count++, offset, {});
2652 break;
2653 }
2654
2655 case 1:
2656 {
2657 constexpr unsigned int d1 = 1;
2658
2659 for (unsigned int i1 = 0; i1 < n_subdivisions; ++i1)
2660 {
2661 const unsigned int offset =
2662 first_vertex_of_patch + i1 * d1;
2663 out.template write_cell<1>(count++, offset, {{d1}});
2665
2666 break;
2667 }
2668
2669 case 2:
2670 {
2671 constexpr unsigned int d1 = 1;
2672 const unsigned int d2 = n;
2673
2674 for (unsigned int i2 = 0; i2 < n_subdivisions; ++i2)
2675 for (unsigned int i1 = 0; i1 < n_subdivisions; ++i1)
2676 {
2677 const unsigned int offset =
2678 first_vertex_of_patch + i2 * d2 + i1 * d1;
2679 out.template write_cell<2>(count++,
2680 offset,
2681 {{d1, d2}});
2682 }
2683
2684 break;
2686
2687 case 3:
2688 {
2689 constexpr unsigned int d1 = 1;
2690 const unsigned int d2 = n;
2691 const unsigned int d3 = n * n;
2692
2693 for (unsigned int i3 = 0; i3 < n_subdivisions; ++i3)
2694 for (unsigned int i2 = 0; i2 < n_subdivisions; ++i2)
2695 for (unsigned int i1 = 0; i1 < n_subdivisions; ++i1)
2696 {
2697 const unsigned int offset = first_vertex_of_patch +
2698 i3 * d3 + i2 * d2 +
2699 i1 * d1;
2700 out.template write_cell<3>(count++,
2701 offset,
2702 {{d1, d2, d3}});
2703 }
2704
2705 break;
2706 }
2707 default:
2709 }
2710
2711 // Update the number of the first vertex of this patch
2712 first_vertex_of_patch +=
2713 Utilities::fixed_power<dim>(n_subdivisions + 1);
2714 }
2715 }
2717 out.flush_cells();
2718 }
2719
2720
2721
2722 template <int dim, int spacedim, typename StreamType>
2723 void
2725 StreamType &out,
2726 const bool legacy_format)
2727 {
2728 Assert(dim <= 3 && dim > 1, ExcNotImplemented());
2729 unsigned int first_vertex_of_patch = 0;
2730 // Array to hold all the node numbers of a cell
2731 std::vector<unsigned> connectivity;
2732
2733 for (const auto &patch : patches)
2734 {
2735 if (patch.reference_cell != ReferenceCells::get_hypercube<dim>())
2736 {
2737 connectivity.resize(patch.data.n_cols());
2738
2739 for (unsigned int i = 0; i < patch.data.n_cols(); ++i)
2740 connectivity[i] = i;
2741
2742 out.template write_high_order_cell<dim>(first_vertex_of_patch,
2743 connectivity);
2744
2745 first_vertex_of_patch += patch.data.n_cols();
2746 }
2747 else
2748 {
2749 const unsigned int n_subdivisions = patch.n_subdivisions;
2750 const unsigned int n = n_subdivisions + 1;
2751
2752 connectivity.resize(Utilities::fixed_power<dim>(n));
2753
2754 switch (dim)
2755 {
2756 case 0:
2757 {
2758 Assert(false,
2759 ExcMessage("Point-like cells should not be possible "
2760 "when writing higher-order cells."));
2761 break;
2762 }
2763 case 1:
2764 {
2765 for (unsigned int i1 = 0; i1 < n_subdivisions + 1; ++i1)
2766 {
2767 const unsigned int local_index = i1;
2768 const unsigned int connectivity_index =
2769 patch.reference_cell
2770 .template vtk_lexicographic_to_node_index<1>(
2771 {{i1}}, {{n_subdivisions}}, legacy_format);
2772 connectivity[connectivity_index] = local_index;
2773 }
2774
2775 break;
2776 }
2777 case 2:
2778 {
2779 for (unsigned int i2 = 0; i2 < n_subdivisions + 1; ++i2)
2780 for (unsigned int i1 = 0; i1 < n_subdivisions + 1; ++i1)
2781 {
2782 const unsigned int local_index = i2 * n + i1;
2783 const unsigned int connectivity_index =
2784 patch.reference_cell
2785 .template vtk_lexicographic_to_node_index<2>(
2786 {{i1, i2}},
2787 {{n_subdivisions, n_subdivisions}},
2788 legacy_format);
2789 connectivity[connectivity_index] = local_index;
2790 }
2791
2792 break;
2793 }
2794 case 3:
2795 {
2796 for (unsigned int i3 = 0; i3 < n_subdivisions + 1; ++i3)
2797 for (unsigned int i2 = 0; i2 < n_subdivisions + 1; ++i2)
2798 for (unsigned int i1 = 0; i1 < n_subdivisions + 1; ++i1)
2799 {
2800 const unsigned int local_index =
2801 i3 * n * n + i2 * n + i1;
2802 const unsigned int connectivity_index =
2803 patch.reference_cell
2804 .template vtk_lexicographic_to_node_index<3>(
2805 {{i1, i2, i3}},
2806 {{n_subdivisions,
2807 n_subdivisions,
2808 n_subdivisions}},
2809 legacy_format);
2810 connectivity[connectivity_index] = local_index;
2811 }
2812
2813 break;
2814 }
2815 default:
2817 }
2818
2819 // Having so set up the 'connectivity' data structure,
2820 // output it:
2821 out.template write_high_order_cell<dim>(first_vertex_of_patch,
2822 connectivity);
2823
2824 // Finally update the number of the first vertex of this patch
2825 first_vertex_of_patch += Utilities::fixed_power<dim>(n);
2826 }
2827 }
2828
2829 out.flush_cells();
2831
2832
2833 template <int dim, int spacedim, typename StreamType>
2834 void
2835 write_data(const std::vector<Patch<dim, spacedim>> &patches,
2836 unsigned int n_data_sets,
2837 const bool double_precision,
2838 StreamType &out)
2839 {
2840 Assert(dim <= 3, ExcNotImplemented());
2841 unsigned int count = 0;
2842
2843 for (const auto &patch : patches)
2844 {
2845 const unsigned int n_subdivisions = patch.n_subdivisions;
2846 const unsigned int n = n_subdivisions + 1;
2847 // Length of loops in all dimensions
2848 Assert((patch.data.n_rows() == n_data_sets &&
2849 !patch.points_are_available) ||
2850 (patch.data.n_rows() == n_data_sets + spacedim &&
2851 patch.points_are_available),
2853 (n_data_sets + spacedim) :
2854 n_data_sets,
2855 patch.data.n_rows()));
2856 Assert(patch.data.n_cols() == Utilities::fixed_power<dim>(n),
2857 ExcInvalidDatasetSize(patch.data.n_cols(), n));
2858
2859 std::vector<float> floats(n_data_sets);
2860 std::vector<double> doubles(n_data_sets);
2861
2862 // Data is already in lexicographic ordering
2863 for (unsigned int i = 0; i < Utilities::fixed_power<dim>(n);
2864 ++i, ++count)
2865 if (double_precision)
2866 {
2867 for (unsigned int data_set = 0; data_set < n_data_sets;
2868 ++data_set)
2869 doubles[data_set] = patch.data(data_set, i);
2870 out.write_dataset(count, doubles);
2871 }
2872 else
2873 {
2874 for (unsigned int data_set = 0; data_set < n_data_sets;
2875 ++data_set)
2876 floats[data_set] = patch.data(data_set, i);
2877 out.write_dataset(count, floats);
2878 }
2879 }
2880 }
2881
2882
2883
2884 namespace
2885 {
2894 Point<2>
2895 svg_project_point(Point<3> point,
2896 Point<3> camera_position,
2897 Point<3> camera_direction,
2898 Point<3> camera_horizontal,
2899 float camera_focus)
2900 {
2901 Point<3> camera_vertical;
2902 camera_vertical[0] = camera_horizontal[1] * camera_direction[2] -
2903 camera_horizontal[2] * camera_direction[1];
2904 camera_vertical[1] = camera_horizontal[2] * camera_direction[0] -
2905 camera_horizontal[0] * camera_direction[2];
2906 camera_vertical[2] = camera_horizontal[0] * camera_direction[1] -
2907 camera_horizontal[1] * camera_direction[0];
2908
2909 float phi;
2910 phi = camera_focus;
2911 phi /= (point[0] - camera_position[0]) * camera_direction[0] +
2912 (point[1] - camera_position[1]) * camera_direction[1] +
2913 (point[2] - camera_position[2]) * camera_direction[2];
2914
2915 Point<3> projection;
2916 projection[0] =
2917 camera_position[0] + phi * (point[0] - camera_position[0]);
2918 projection[1] =
2919 camera_position[1] + phi * (point[1] - camera_position[1]);
2920 projection[2] =
2921 camera_position[2] + phi * (point[2] - camera_position[2]);
2922
2923 Point<2> projection_decomposition;
2924 projection_decomposition[0] = (projection[0] - camera_position[0] -
2925 camera_focus * camera_direction[0]) *
2926 camera_horizontal[0];
2927 projection_decomposition[0] += (projection[1] - camera_position[1] -
2928 camera_focus * camera_direction[1]) *
2929 camera_horizontal[1];
2930 projection_decomposition[0] += (projection[2] - camera_position[2] -
2931 camera_focus * camera_direction[2]) *
2932 camera_horizontal[2];
2933
2934 projection_decomposition[1] = (projection[0] - camera_position[0] -
2935 camera_focus * camera_direction[0]) *
2936 camera_vertical[0];
2937 projection_decomposition[1] += (projection[1] - camera_position[1] -
2938 camera_focus * camera_direction[1]) *
2939 camera_vertical[1];
2940 projection_decomposition[1] += (projection[2] - camera_position[2] -
2941 camera_focus * camera_direction[2]) *
2942 camera_vertical[2];
2943
2944 return projection_decomposition;
2945 }
2946
2947
2952 Point<6>
2953 svg_get_gradient_parameters(Point<3> points[])
2954 {
2955 Point<3> v_min, v_max, v_inter;
2956
2957 // Use the Bubblesort algorithm to sort the points with respect to the
2958 // third coordinate
2959 for (int i = 0; i < 2; ++i)
2960 {
2961 for (int j = 0; j < 2 - i; ++j)
2963 if (points[j][2] > points[j + 1][2])
2964 {
2965 Point<3> temp = points[j];
2966 points[j] = points[j + 1];
2967 points[j + 1] = temp;
2968 }
2969 }
2970 }
2971
2972 // save the related three-dimensional vectors v_min, v_inter, and v_max
2973 v_min = points[0];
2974 v_inter = points[1];
2975 v_max = points[2];
2976
2977 Point<2> A[2];
2978 Point<2> b, gradient;
2979
2980 // determine the plane offset c
2981 A[0][0] = v_max[0] - v_min[0];
2982 A[0][1] = v_inter[0] - v_min[0];
2983 A[1][0] = v_max[1] - v_min[1];
2984 A[1][1] = v_inter[1] - v_min[1];
2985
2986 b[0] = -v_min[0];
2987 b[1] = -v_min[1];
2988
2989 double x, sum;
2990 bool col_change = false;
2991
2992 if (A[0][0] == 0)
2993 {
2994 col_change = true;
2995
2996 A[0][0] = A[0][1];
2997 A[0][1] = 0;
2998
2999 double temp = A[1][0];
3000 A[1][0] = A[1][1];
3001 A[1][1] = temp;
3002 }
3004 for (unsigned int k = 0; k < 1; ++k)
3005 {
3006 for (unsigned int i = k + 1; i < 2; ++i)
3007 {
3008 x = A[i][k] / A[k][k];
3009
3010 for (unsigned int j = k + 1; j < 2; ++j)
3011 A[i][j] = A[i][j] - A[k][j] * x;
3012
3013 b[i] = b[i] - b[k] * x;
3014 }
3015 }
3016
3017 b[1] = b[1] / A[1][1];
3018
3019 for (int i = 0; i >= 0; i--)
3020 {
3021 sum = b[i];
3022
3023 for (unsigned int j = i + 1; j < 2; ++j)
3024 sum = sum - A[i][j] * b[j];
3025
3026 b[i] = sum / A[i][i];
3027 }
3028
3029 if (col_change)
3030 {
3031 double temp = b[0];
3032 b[0] = b[1];
3033 b[1] = temp;
3034 }
3035
3036 double c = b[0] * (v_max[2] - v_min[2]) + b[1] * (v_inter[2] - v_min[2]) +
3037 v_min[2];
3038
3039 // Determine the first entry of the gradient (phi, cf. documentation)
3040 A[0][0] = v_max[0] - v_min[0];
3041 A[0][1] = v_inter[0] - v_min[0];
3042 A[1][0] = v_max[1] - v_min[1];
3043 A[1][1] = v_inter[1] - v_min[1];
3044
3045 b[0] = 1.0 - v_min[0];
3046 b[1] = -v_min[1];
3047
3048 col_change = false;
3049
3050 if (A[0][0] == 0)
3051 {
3052 col_change = true;
3053
3054 A[0][0] = A[0][1];
3055 A[0][1] = 0;
3056
3057 double temp = A[1][0];
3058 A[1][0] = A[1][1];
3059 A[1][1] = temp;
3060 }
3061
3062 for (unsigned int k = 0; k < 1; ++k)
3063 {
3064 for (unsigned int i = k + 1; i < 2; ++i)
3065 {
3066 x = A[i][k] / A[k][k];
3067
3068 for (unsigned int j = k + 1; j < 2; ++j)
3069 A[i][j] = A[i][j] - A[k][j] * x;
3070
3071 b[i] = b[i] - b[k] * x;
3072 }
3073 }
3074
3075 b[1] = b[1] / A[1][1];
3076
3077 for (int i = 0; i >= 0; i--)
3078 {
3079 sum = b[i];
3080
3081 for (unsigned int j = i + 1; j < 2; ++j)
3082 sum = sum - A[i][j] * b[j];
3083
3084 b[i] = sum / A[i][i];
3085 }
3086
3087 if (col_change)
3088 {
3089 double temp = b[0];
3090 b[0] = b[1];
3091 b[1] = temp;
3092 }
3093
3094 gradient[0] = b[0] * (v_max[2] - v_min[2]) +
3095 b[1] * (v_inter[2] - v_min[2]) - c + v_min[2];
3096
3097 // determine the second entry of the gradient
3098 A[0][0] = v_max[0] - v_min[0];
3099 A[0][1] = v_inter[0] - v_min[0];
3100 A[1][0] = v_max[1] - v_min[1];
3101 A[1][1] = v_inter[1] - v_min[1];
3102
3103 b[0] = -v_min[0];
3104 b[1] = 1.0 - v_min[1];
3105
3106 col_change = false;
3107
3108 if (A[0][0] == 0)
3109 {
3110 col_change = true;
3111
3112 A[0][0] = A[0][1];
3113 A[0][1] = 0;
3114
3115 double temp = A[1][0];
3116 A[1][0] = A[1][1];
3117 A[1][1] = temp;
3118 }
3119
3120 for (unsigned int k = 0; k < 1; ++k)
3121 {
3122 for (unsigned int i = k + 1; i < 2; ++i)
3123 {
3124 x = A[i][k] / A[k][k];
3125
3126 for (unsigned int j = k + 1; j < 2; ++j)
3127 A[i][j] = A[i][j] - A[k][j] * x;
3128
3129 b[i] = b[i] - b[k] * x;
3130 }
3131 }
3132
3133 b[1] = b[1] / A[1][1];
3134
3135 for (int i = 0; i >= 0; i--)
3136 {
3137 sum = b[i];
3138
3139 for (unsigned int j = i + 1; j < 2; ++j)
3140 sum = sum - A[i][j] * b[j];
3141
3142 b[i] = sum / A[i][i];
3143 }
3144
3145 if (col_change)
3146 {
3147 double temp = b[0];
3148 b[0] = b[1];
3149 b[1] = temp;
3150 }
3151
3152 gradient[1] = b[0] * (v_max[2] - v_min[2]) +
3153 b[1] * (v_inter[2] - v_min[2]) - c + v_min[2];
3154
3155 // normalize the gradient
3156 gradient /= gradient.norm();
3157
3158 const double lambda = -gradient[0] * (v_min[0] - v_max[0]) -
3159 gradient[1] * (v_min[1] - v_max[1]);
3160
3161 Point<6> gradient_parameters;
3162
3163 gradient_parameters[0] = v_min[0];
3164 gradient_parameters[1] = v_min[1];
3165
3166 gradient_parameters[2] = v_min[0] + lambda * gradient[0];
3167 gradient_parameters[3] = v_min[1] + lambda * gradient[1];
3168
3169 gradient_parameters[4] = v_min[2];
3170 gradient_parameters[5] = v_max[2];
3171
3172 return gradient_parameters;
3173 }
3174 } // namespace
3175
3176
3177
3178 template <int dim, int spacedim>
3179 void
3181 const std::vector<Patch<dim, spacedim>> &patches,
3182 const std::vector<std::string> &data_names,
3183 const std::vector<
3184 std::tuple<unsigned int,
3185 unsigned int,
3186 std::string,
3188 const UcdFlags &flags,
3189 std::ostream &out)
3190 {
3191 // Note that while in theory dim==0 should be implemented, this is not
3192 // tested, therefore currently not allowed.
3193 AssertThrow(dim > 0, ExcNotImplemented());
3194
3195 AssertThrow(out.fail() == false, ExcIO());
3196
3197#ifndef DEAL_II_WITH_MPI
3198 // verify that there are indeed patches to be written out. most of the
3199 // times, people just forget to call build_patches when there are no
3200 // patches, so a warning is in order. that said, the assertion is disabled
3201 // if we support MPI since then it can happen that on the coarsest mesh, a
3202 // processor simply has no cells it actually owns, and in that case it is
3203 // legit if there are no patches
3204 Assert(patches.size() > 0, ExcNoPatches());
3205#else
3206 if (patches.empty())
3207 return;
3208#endif
3209
3210 const unsigned int n_data_sets = data_names.size();
3211
3212 UcdStream ucd_out(out, flags);
3213
3214 // first count the number of cells and cells for later use
3215 unsigned int n_nodes;
3216 unsigned int n_cells;
3217 std::tie(n_nodes, n_cells) = count_nodes_and_cells(patches);
3218 //---------------------
3219 // preamble
3220 if (flags.write_preamble)
3221 {
3222 out
3223 << "# This file was generated by the deal.II library." << '\n'
3224 << "# Date = " << Utilities::System::get_date() << '\n'
3225 << "# Time = " << Utilities::System::get_time() << '\n'
3226 << "#" << '\n'
3227 << "# For a description of the UCD format see the AVS Developer's guide."
3228 << '\n'
3229 << "#" << '\n';
3230 }
3231
3232 // start with ucd data
3233 out << n_nodes << ' ' << n_cells << ' ' << n_data_sets << ' ' << 0
3234 << ' ' // no cell data at present
3235 << 0 // no model data
3236 << '\n';
3237
3238 write_nodes(patches, ucd_out);
3239 out << '\n';
3240
3241 write_cells(patches, ucd_out);
3242 out << '\n';
3243
3244 //---------------------------
3245 // now write data
3246 if (n_data_sets != 0)
3247 {
3248 out << n_data_sets << " "; // number of vectors
3249 for (unsigned int i = 0; i < n_data_sets; ++i)
3250 out << 1 << ' '; // number of components;
3251 // only 1 supported presently
3252 out << '\n';
3253
3254 for (unsigned int data_set = 0; data_set < n_data_sets; ++data_set)
3255 out << data_names[data_set]
3256 << ",dimensionless" // no units supported at present
3257 << '\n';
3258
3259 write_data(patches, n_data_sets, true, ucd_out);
3260 }
3261 // make sure everything now gets to disk
3262 out.flush();
3263
3264 // assert the stream is still ok
3265 AssertThrow(out.fail() == false, ExcIO());
3266 }
3267
3268
3269 template <int dim, int spacedim>
3270 void
3272 const std::vector<Patch<dim, spacedim>> &patches,
3273 const std::vector<std::string> &data_names,
3274 const std::vector<
3275 std::tuple<unsigned int,
3276 unsigned int,
3277 std::string,
3279 const DXFlags &flags,
3280 std::ostream &out)
3281 {
3282 // Point output is currently not implemented.
3283 AssertThrow(dim > 0, ExcNotImplemented());
3284
3285 AssertThrow(out.fail() == false, ExcIO());
3286
3287#ifndef DEAL_II_WITH_MPI
3288 // verify that there are indeed patches to be written out. most of the
3289 // times, people just forget to call build_patches when there are no
3290 // patches, so a warning is in order. that said, the assertion is disabled
3291 // if we support MPI since then it can happen that on the coarsest mesh, a
3292 // processor simply has no cells it actually owns, and in that case it is
3293 // legit if there are no patches
3294 Assert(patches.size() > 0, ExcNoPatches());
3295#else
3296 if (patches.empty())
3297 return;
3298#endif
3299 // Stream with special features for dx output
3300 DXStream dx_out(out, flags);
3301
3302 // Variable counting the offset of binary data.
3303 unsigned int offset = 0;
3304
3305 const unsigned int n_data_sets = data_names.size();
3306
3307 // first count the number of cells and cells for later use
3308 unsigned int n_nodes;
3309 unsigned int n_cells;
3310 std::tie(n_nodes, n_cells) = count_nodes_and_cells(patches);
3311
3312 // start with vertices order is lexicographical, x varying fastest
3313 out << "object \"vertices\" class array type float rank 1 shape "
3314 << spacedim << " items " << n_nodes;
3315
3316 if (flags.coordinates_binary)
3317 {
3318 out << " lsb ieee data 0" << '\n';
3319 offset += n_nodes * spacedim * sizeof(float);
3320 }
3321 else
3322 {
3323 out << " data follows" << '\n';
3324 write_nodes(patches, dx_out);
3325 }
3326
3327 //-----------------------------
3328 // first write the coordinates of all vertices
3329
3330 //---------------------------------------
3331 // write cells
3332 out << "object \"cells\" class array type int rank 1 shape "
3333 << GeometryInfo<dim>::vertices_per_cell << " items " << n_cells;
3334
3335 if (flags.int_binary)
3336 {
3337 out << " lsb binary data " << offset << '\n';
3338 offset += n_cells * sizeof(int);
3339 }
3340 else
3341 {
3342 out << " data follows" << '\n';
3343 write_cells(patches, dx_out);
3344 out << '\n';
3345 }
3346
3347
3348 out << "attribute \"element type\" string \"";
3349 if (dim == 1)
3350 out << "lines";
3351 if (dim == 2)
3352 out << "quads";
3353 if (dim == 3)
3354 out << "cubes";
3355 out << "\"" << '\n' << "attribute \"ref\" string \"positions\"" << '\n';
3356
3357 // TODO:[GK] Patches must be of same size!
3358 //---------------------------
3359 // write neighbor information
3360 if (flags.write_neighbors)
3361 {
3362 out << "object \"neighbors\" class array type int rank 1 shape "
3363 << GeometryInfo<dim>::faces_per_cell << " items " << n_cells
3364 << " data follows";
3365
3366 for (const auto &patch : patches)
3367 {
3368 const unsigned int n = patch.n_subdivisions;
3369 const unsigned int n1 = (dim > 0) ? n : 1;
3370 const unsigned int n2 = (dim > 1) ? n : 1;
3371 const unsigned int n3 = (dim > 2) ? n : 1;
3372 const unsigned int x_minus = (dim > 0) ? 0 : 0;
3373 const unsigned int x_plus = (dim > 0) ? 1 : 0;
3374 const unsigned int y_minus = (dim > 1) ? 2 : 0;
3375 const unsigned int y_plus = (dim > 1) ? 3 : 0;
3376 const unsigned int z_minus = (dim > 2) ? 4 : 0;
3377 const unsigned int z_plus = (dim > 2) ? 5 : 0;
3378 unsigned int cells_per_patch = Utilities::fixed_power<dim>(n);
3379 unsigned int dx = 1;
3380 unsigned int dy = n;
3381 unsigned int dz = n * n;
3382
3383 const unsigned int patch_start =
3384 patch.patch_index * cells_per_patch;
3385
3386 for (unsigned int i3 = 0; i3 < n3; ++i3)
3387 for (unsigned int i2 = 0; i2 < n2; ++i2)
3388 for (unsigned int i1 = 0; i1 < n1; ++i1)
3389 {
3390 const unsigned int nx = i1 * dx;
3391 const unsigned int ny = i2 * dy;
3392 const unsigned int nz = i3 * dz;
3393
3394 // There are no neighbors for dim==0. Note that this case is
3395 // caught by the AssertThrow at the beginning of this
3396 // function anyway. This condition avoids compiler warnings.
3397 if (dim < 1)
3398 continue;
3399
3400 out << '\n';
3401 // Direction -x Last cell in row of other patch
3402 if (i1 == 0)
3403 {
3404 const unsigned int nn = patch.neighbors[x_minus];
3405 out << '\t';
3406 if (nn != patch.no_neighbor)
3407 out
3408 << (nn * cells_per_patch + ny + nz + dx * (n - 1));
3409 else
3410 out << "-1";
3411 }
3412 else
3413 {
3414 out << '\t' << patch_start + nx - dx + ny + nz;
3415 }
3416 // Direction +x First cell in row of other patch
3417 if (i1 == n - 1)
3418 {
3419 const unsigned int nn = patch.neighbors[x_plus];
3420 out << '\t';
3421 if (nn != patch.no_neighbor)
3422 out << (nn * cells_per_patch + ny + nz);
3423 else
3424 out << "-1";
3425 }
3426 else
3427 {
3428 out << '\t' << patch_start + nx + dx + ny + nz;
3429 }
3430 if (dim < 2)
3431 continue;
3432 // Direction -y
3433 if (i2 == 0)
3434 {
3435 const unsigned int nn = patch.neighbors[y_minus];
3436 out << '\t';
3437 if (nn != patch.no_neighbor)
3438 out
3439 << (nn * cells_per_patch + nx + nz + dy * (n - 1));
3440 else
3441 out << "-1";
3442 }
3443 else
3444 {
3445 out << '\t' << patch_start + nx + ny - dy + nz;
3446 }
3447 // Direction +y
3448 if (i2 == n - 1)
3449 {
3450 const unsigned int nn = patch.neighbors[y_plus];
3451 out << '\t';
3452 if (nn != patch.no_neighbor)
3453 out << (nn * cells_per_patch + nx + nz);
3454 else
3455 out << "-1";
3456 }
3457 else
3458 {
3459 out << '\t' << patch_start + nx + ny + dy + nz;
3460 }
3461 if (dim < 3)
3462 continue;
3463
3464 // Direction -z
3465 if (i3 == 0)
3466 {
3467 const unsigned int nn = patch.neighbors[z_minus];
3468 out << '\t';
3469 if (nn != patch.no_neighbor)
3470 out
3471 << (nn * cells_per_patch + nx + ny + dz * (n - 1));
3472 else
3473 out << "-1";
3474 }
3475 else
3476 {
3477 out << '\t' << patch_start + nx + ny + nz - dz;
3478 }
3479 // Direction +z
3480 if (i3 == n - 1)
3481 {
3482 const unsigned int nn = patch.neighbors[z_plus];
3483 out << '\t';
3484 if (nn != patch.no_neighbor)
3485 out << (nn * cells_per_patch + nx + ny);
3486 else
3487 out << "-1";
3488 }
3489 else
3490 {
3491 out << '\t' << patch_start + nx + ny + nz + dz;
3492 }
3493 }
3494 out << '\n';
3495 }
3496 }
3497 //---------------------------
3498 // now write data
3499 if (n_data_sets != 0)
3500 {
3501 out << "object \"data\" class array type float rank 1 shape "
3502 << n_data_sets << " items " << n_nodes;
3503
3504 if (flags.data_binary)
3505 {
3506 out << " lsb ieee data " << offset << '\n';
3507 offset += n_data_sets * n_nodes *
3508 ((flags.data_double) ? sizeof(double) : sizeof(float));
3509 }
3510 else
3511 {
3512 out << " data follows" << '\n';
3513 write_data(patches, n_data_sets, flags.data_double, dx_out);
3514 }
3515
3516 // loop over all patches
3517 out << "attribute \"dep\" string \"positions\"" << '\n';
3518 }
3519 else
3520 {
3521 out << "object \"data\" class constantarray type float rank 0 items "
3522 << n_nodes << " data follows" << '\n'
3523 << '0' << '\n';
3524 }
3525
3526 // no model data
3527
3528 out << "object \"deal data\" class field" << '\n'
3529 << "component \"positions\" value \"vertices\"" << '\n'
3530 << "component \"connections\" value \"cells\"" << '\n'
3531 << "component \"data\" value \"data\"" << '\n';
3532
3533 if (flags.write_neighbors)
3534 out << "component \"neighbors\" value \"neighbors\"" << '\n';
3535
3536 {
3537 out << "attribute \"created\" string \"" << Utilities::System::get_date()
3538 << ' ' << Utilities::System::get_time() << '"' << '\n';
3539 }
3540
3541 out << "end" << '\n';
3542 // Write all binary data now
3543 if (flags.coordinates_binary)
3544 write_nodes(patches, dx_out);
3545 if (flags.int_binary)
3546 write_cells(patches, dx_out);
3547 if (flags.data_binary)
3548 write_data(patches, n_data_sets, flags.data_double, dx_out);
3549
3550 // make sure everything now gets to disk
3551 out.flush();
3552
3553 // assert the stream is still ok
3554 AssertThrow(out.fail() == false, ExcIO());
3555 }
3556
3557
3558
3559 template <int dim, int spacedim>
3560 void
3562 const std::vector<Patch<dim, spacedim>> &patches,
3563 const std::vector<std::string> &data_names,
3564 const std::vector<
3565 std::tuple<unsigned int,
3566 unsigned int,
3567 std::string,
3569 const GnuplotFlags &flags,
3570 std::ostream &out)
3571 {
3572 AssertThrow(out.fail() == false, ExcIO());
3573
3574#ifndef DEAL_II_WITH_MPI
3575 // verify that there are indeed patches to be written out. most
3576 // of the times, people just forget to call build_patches when there
3577 // are no patches, so a warning is in order. that said, the
3578 // assertion is disabled if we support MPI since then it can
3579 // happen that on the coarsest mesh, a processor simply has no
3580 // cells it actually owns, and in that case it is legit if there
3581 // are no patches
3582 Assert(patches.size() > 0, ExcNoPatches());
3583#else
3584 if (patches.empty())
3585 return;
3586#endif
3587
3588 const unsigned int n_data_sets = data_names.size();
3589
3590 // write preamble
3591 {
3592 out << "# This file was generated by the deal.II library." << '\n'
3593 << "# Date = " << Utilities::System::get_date() << '\n'
3594 << "# Time = " << Utilities::System::get_time() << '\n'
3595 << "#" << '\n'
3596 << "# For a description of the GNUPLOT format see the GNUPLOT manual."
3597 << '\n'
3598 << "#" << '\n'
3599 << "# ";
3600
3601 AssertThrow(spacedim <= flags.space_dimension_labels.size(),
3603 for (unsigned int spacedim_n = 0; spacedim_n < spacedim; ++spacedim_n)
3604 {
3605 out << '<' << flags.space_dimension_labels.at(spacedim_n) << "> ";
3606 }
3607
3608 for (const auto &data_name : data_names)
3609 out << '<' << data_name << "> ";
3610 out << '\n';
3611 }
3612
3613
3614 // loop over all patches
3615 for (const auto &patch : patches)
3616 {
3617 const unsigned int n_subdivisions = patch.n_subdivisions;
3618 const unsigned int n_points_per_direction = n_subdivisions + 1;
3619
3620 Assert((patch.data.n_rows() == n_data_sets &&
3621 !patch.points_are_available) ||
3622 (patch.data.n_rows() == n_data_sets + spacedim &&
3623 patch.points_are_available),
3625 (n_data_sets + spacedim) :
3626 n_data_sets,
3627 patch.data.n_rows()));
3628
3629 auto output_point_data =
3630 [&out, &patch, n_data_sets](const unsigned int point_index) mutable {
3631 for (unsigned int data_set = 0; data_set < n_data_sets; ++data_set)
3632 out << patch.data(data_set, point_index) << ' ';
3633 };
3634
3635 switch (dim)
3636 {
3637 case 0:
3638 {
3639 Assert(patch.reference_cell == ReferenceCells::Vertex,
3641 Assert(patch.data.n_cols() == 1,
3642 ExcInvalidDatasetSize(patch.data.n_cols(),
3643 n_subdivisions + 1));
3644
3645
3646 // compute coordinates for this patch point
3647 out << get_equispaced_location(patch, {}, n_subdivisions)
3648 << ' ';
3649 output_point_data(0);
3650 out << '\n';
3651 out << '\n';
3652 break;
3653 }
3654
3655 case 1:
3656 {
3657 Assert(patch.reference_cell == ReferenceCells::Line,
3659 Assert(patch.data.n_cols() ==
3660 Utilities::fixed_power<dim>(n_points_per_direction),
3661 ExcInvalidDatasetSize(patch.data.n_cols(),
3662 n_subdivisions + 1));
3663
3664 for (unsigned int i1 = 0; i1 < n_points_per_direction; ++i1)
3665 {
3666 // compute coordinates for this patch point
3667 out << get_equispaced_location(patch, {i1}, n_subdivisions)
3668 << ' ';
3669
3670 output_point_data(i1);
3671 out << '\n';
3672 }
3673 // end of patch
3674 out << '\n';
3675 out << '\n';
3676 break;
3677 }
3678
3679 case 2:
3680 {
3681 if (patch.reference_cell == ReferenceCells::Quadrilateral)
3682 {
3683 Assert(patch.data.n_cols() == Utilities::fixed_power<dim>(
3684 n_points_per_direction),
3685 ExcInvalidDatasetSize(patch.data.n_cols(),
3686 n_subdivisions + 1));
3687
3688 for (unsigned int i2 = 0; i2 < n_points_per_direction; ++i2)
3689 {
3690 for (unsigned int i1 = 0; i1 < n_points_per_direction;
3691 ++i1)
3692 {
3693 // compute coordinates for this patch point
3694 out << get_equispaced_location(patch,
3695 {i1, i2},
3696 n_subdivisions)
3697 << ' ';
3698
3699 output_point_data(i1 + i2 * n_points_per_direction);
3700 out << '\n';
3701 }
3702 // end of row in patch
3703 out << '\n';
3704 }
3705 }
3706 else if (patch.reference_cell == ReferenceCells::Triangle)
3707 {
3708 Assert(n_subdivisions == 1, ExcNotImplemented());
3709
3710 Assert(patch.data.n_cols() == 3, ExcInternalError());
3711
3712 // Gnuplot can only plot surfaces if each facet of the
3713 // surface is a bilinear patch, or a subdivided bilinear
3714 // patch with equally many points along each row of the
3715 // subdivision. This is what the code above for
3716 // quadrilaterals does. We emulate this by repeating the
3717 // third point of a triangle twice so that there are two
3718 // points for that row as well -- i.e., we write a 2x2
3719 // bilinear patch where two of the points are collapsed onto
3720 // one vertex.
3721 //
3722 // This also matches the example here:
3723 // https://stackoverflow.com/questions/42784369/drawing-triangular-mesh-using-gnuplot
3724 out << get_node_location(patch, 0) << ' ';
3725 output_point_data(0);
3726 out << '\n';
3727
3728 out << get_node_location(patch, 1) << ' ';
3729 output_point_data(1);
3730 out << '\n';
3731 out << '\n'; // end of one row of points
3732
3733 out << get_node_location(patch, 2) << ' ';
3734 output_point_data(2);
3735 out << '\n';
3736
3737 out << get_node_location(patch, 2) << ' ';
3738 output_point_data(2);
3739 out << '\n';
3740 out << '\n'; // end of the second row of points
3741 out << '\n'; // end of the entire patch
3742 }
3743 else
3744 // There aren't any other reference cells in 2d than the
3745 // quadrilateral and the triangle. So whatever we got here
3746 // can't be any good
3748 // end of patch
3749 out << '\n';
3750
3751 break;
3752 }
3753
3754 case 3:
3755 {
3756 if (patch.reference_cell == ReferenceCells::Hexahedron)
3757 {
3758 Assert(patch.data.n_cols() == Utilities::fixed_power<dim>(
3759 n_points_per_direction),
3760 ExcInvalidDatasetSize(patch.data.n_cols(),
3761 n_subdivisions + 1));
3762
3763 // for all grid points: draw lines into all positive
3764 // coordinate directions if there is another grid point
3765 // there
3766 for (unsigned int i3 = 0; i3 < n_points_per_direction; ++i3)
3767 for (unsigned int i2 = 0; i2 < n_points_per_direction;
3768 ++i2)
3769 for (unsigned int i1 = 0; i1 < n_points_per_direction;
3770 ++i1)
3771 {
3772 // compute coordinates for this patch point
3773 const Point<spacedim> this_point =
3774 get_equispaced_location(patch,
3775 {i1, i2, i3},
3776 n_subdivisions);
3777 // line into positive x-direction if possible
3778 if (i1 < n_subdivisions)
3779 {
3780 // write point here and its data
3781 out << this_point << ' ';
3782 output_point_data(i1 +
3783 i2 * n_points_per_direction +
3784 i3 * n_points_per_direction *
3785 n_points_per_direction);
3786 out << '\n';
3787
3788 // write point there and its data
3789 out << get_equispaced_location(patch,
3790 {i1 + 1, i2, i3},
3791 n_subdivisions)
3792 << ' ';
3793
3794 output_point_data((i1 + 1) +
3795 i2 * n_points_per_direction +
3796 i3 * n_points_per_direction *
3797 n_points_per_direction);
3798 out << '\n';
3799
3800 // end of line
3801 out << '\n' << '\n';
3802 }
3803
3804 // line into positive y-direction if possible
3805 if (i2 < n_subdivisions)
3806 {
3807 // write point here and its data
3808 out << this_point << ' ';
3809 output_point_data(i1 +
3810 i2 * n_points_per_direction +
3811 i3 * n_points_per_direction *
3812 n_points_per_direction);
3813 out << '\n';
3814
3815 // write point there and its data
3816 out << get_equispaced_location(patch,
3817 {i1, i2 + 1, i3},
3818 n_subdivisions)
3819 << ' ';
3820
3821 output_point_data(
3822 i1 + (i2 + 1) * n_points_per_direction +
3823 i3 * n_points_per_direction *
3824 n_points_per_direction);
3825 out << '\n';
3826
3827 // end of line
3828 out << '\n' << '\n';
3829 }
3830
3831 // line into positive z-direction if possible
3832 if (i3 < n_subdivisions)
3833 {
3834 // write point here and its data
3835 out << this_point << ' ';
3836 output_point_data(i1 +
3837 i2 * n_points_per_direction +
3838 i3 * n_points_per_direction *
3839 n_points_per_direction);
3840 out << '\n';
3841
3842 // write point there and its data
3843 out << get_equispaced_location(patch,
3844 {i1, i2, i3 + 1},
3845 n_subdivisions)
3846 << ' ';
3847
3848 output_point_data(
3849 i1 + i2 * n_points_per_direction +
3850 (i3 + 1) * n_points_per_direction *
3851 n_points_per_direction);
3852 out << '\n';
3853 // end of line
3854 out << '\n' << '\n';
3855 }
3856 }
3857 }
3858 else if (patch.reference_cell == ReferenceCells::Tetrahedron)
3859 {
3860 Assert(n_subdivisions == 1, ExcNotImplemented());
3861
3862 // Draw the tetrahedron as a collection of two lines.
3863 for (const unsigned int v : {0, 1, 2, 0, 3, 2})
3864 {
3865 out << get_node_location(patch, v) << ' ';
3866 output_point_data(v);
3867 out << '\n';
3868 }
3869 out << '\n'; // end of first line
3870
3871 for (const unsigned int v : {3, 1})
3872 {
3873 out << get_node_location(patch, v) << ' ';
3874 output_point_data(v);
3875 out << '\n';
3876 }
3877 out << '\n'; // end of second line
3878 }
3879 else if (patch.reference_cell == ReferenceCells::Pyramid)
3880 {
3881 Assert(n_subdivisions == 1, ExcNotImplemented());
3882
3883 // Draw the pyramid as a collection of two lines.
3884 for (const unsigned int v : {0, 1, 3, 2, 0, 4, 1})
3885 {
3886 out << get_node_location(patch, v) << ' ';
3887 output_point_data(v);
3888 out << '\n';
3889 }
3890 out << '\n'; // end of first line
3891
3892 for (const unsigned int v : {2, 4, 3})
3893 {
3894 out << get_node_location(patch, v) << ' ';
3895 output_point_data(v);
3896 out << '\n';
3897 }
3898 out << '\n'; // end of second line
3899 }
3900 else if (patch.reference_cell == ReferenceCells::Wedge)
3901 {
3902 Assert(n_subdivisions == 1, ExcNotImplemented());
3903
3904 // Draw the wedge as a collection of three
3905 // lines. The first one wraps around the base,
3906 // goes up to the top, and wraps around that. The
3907 // second and third are just individual lines
3908 // going from base to top.
3909 for (const unsigned int v : {0, 1, 2, 0, 3, 4, 5, 3})
3910 {
3911 out << get_node_location(patch, v) << ' ';
3912 output_point_data(v);
3913 out << '\n';
3914 }
3915 out << '\n'; // end of first line
3916
3917 for (const unsigned int v : {1, 4})
3918 {
3919 out << get_node_location(patch, v) << ' ';
3920 output_point_data(v);
3921 out << '\n';
3922 }
3923 out << '\n'; // end of second line
3924
3925 for (const unsigned int v : {2, 5})
3926 {
3927 out << get_node_location(patch, v) << ' ';
3928 output_point_data(v);
3929 out << '\n';
3930 }
3931 out << '\n'; // end of second line
3932 }
3933 else
3934 // No other reference cells are currently implemented
3936
3937 break;
3938 }
3939
3940 default:
3942 }
3943 }
3944 // make sure everything now gets to disk
3945 out.flush();
3946
3947 AssertThrow(out.fail() == false, ExcIO());
3948 }
3949
3950
3951 namespace
3952 {
3953 template <int dim, int spacedim>
3954 void
3955 do_write_povray(const std::vector<Patch<dim, spacedim>> &,
3956 const std::vector<std::string> &,
3957 const PovrayFlags &,
3958 std::ostream &)
3959 {
3960 Assert(false,
3961 ExcMessage("Writing files in POVRAY format is only supported "
3962 "for two-dimensional meshes."));
3963 }
3964
3965
3966
3967 void
3968 do_write_povray(const std::vector<Patch<2, 2>> &patches,
3969 const std::vector<std::string> &data_names,
3970 const PovrayFlags &flags,
3971 std::ostream &out)
3972 {
3973 AssertThrow(out.fail() == false, ExcIO());
3974
3975#ifndef DEAL_II_WITH_MPI
3976 // verify that there are indeed patches to be written out. most
3977 // of the times, people just forget to call build_patches when there
3978 // are no patches, so a warning is in order. that said, the
3979 // assertion is disabled if we support MPI since then it can
3980 // happen that on the coarsest mesh, a processor simply has no cells it
3981 // actually owns, and in that case it is legit if there are no patches
3982 Assert(patches.size() > 0, ExcNoPatches());
3983#else
3984 if (patches.empty())
3985 return;
3986#endif
3987 constexpr int dim = 2;
3988 (void)dim;
3989 constexpr int spacedim = 2;
3990
3991 const unsigned int n_data_sets = data_names.size();
3992 (void)n_data_sets;
3993
3994 // write preamble
3995 {
3996 out
3997 << "/* This file was generated by the deal.II library." << '\n'
3998 << " Date = " << Utilities::System::get_date() << '\n'
3999 << " Time = " << Utilities::System::get_time() << '\n'
4000 << '\n'
4001 << " For a description of the POVRAY format see the POVRAY manual."
4002 << '\n'
4003 << "*/ " << '\n';
4004
4005 // include files
4006 out << "#include \"colors.inc\" " << '\n'
4007 << "#include \"textures.inc\" " << '\n';
4008
4009
4010 // use external include file for textures, camera and light
4011 if (flags.external_data)
4012 out << "#include \"data.inc\" " << '\n';
4013 else // all definitions in data file
4014 {
4015 // camera
4016 out << '\n'
4017 << '\n'
4018 << "camera {" << '\n'
4019 << " location <1,4,-7>" << '\n'
4020 << " look_at <0,0,0>" << '\n'
4021 << " angle 30" << '\n'
4022 << "}" << '\n';
4023
4024 // light
4025 out << '\n'
4026 << "light_source {" << '\n'
4027 << " <1,4,-7>" << '\n'
4028 << " color Grey" << '\n'
4029 << "}" << '\n';
4030 out << '\n'
4031 << "light_source {" << '\n'
4032 << " <0,20,0>" << '\n'
4033 << " color White" << '\n'
4034 << "}" << '\n';
4035 }
4036 }
4037
4038 // max. and min. height of solution
4039 Assert(patches.size() > 0, ExcNoPatches());
4040 double hmin = patches[0].data(0, 0);
4041 double hmax = patches[0].data(0, 0);
4042
4043 for (const auto &patch : patches)
4044 {
4045 const unsigned int n_subdivisions = patch.n_subdivisions;
4046
4047 Assert((patch.data.n_rows() == n_data_sets &&
4048 !patch.points_are_available) ||
4049 (patch.data.n_rows() == n_data_sets + spacedim &&
4050 patch.points_are_available),
4052 (n_data_sets + spacedim) :
4053 n_data_sets,
4054 patch.data.n_rows()));
4055 Assert(patch.data.n_cols() ==
4056 Utilities::fixed_power<dim>(n_subdivisions + 1),
4057 ExcInvalidDatasetSize(patch.data.n_cols(),
4058 n_subdivisions + 1));
4059
4060 for (unsigned int i = 0; i < n_subdivisions + 1; ++i)
4061 for (unsigned int j = 0; j < n_subdivisions + 1; ++j)
4062 {
4063 const int dl = i * (n_subdivisions + 1) + j;
4064 if (patch.data(0, dl) < hmin)
4065 hmin = patch.data(0, dl);
4066 if (patch.data(0, dl) > hmax)
4067 hmax = patch.data(0, dl);
4068 }
4069 }
4070
4071 out << "#declare HMIN=" << hmin << ";" << '\n'
4072 << "#declare HMAX=" << hmax << ";" << '\n'
4073 << '\n';
4074
4075 if (!flags.external_data)
4076 {
4077 // texture with scaled niveau lines 10 lines in the surface
4078 out << "#declare Tex=texture{" << '\n'
4079 << " pigment {" << '\n'
4080 << " gradient y" << '\n'
4081 << " scale y*(HMAX-HMIN)*" << 0.1 << '\n'
4082 << " color_map {" << '\n'
4083 << " [0.00 color Light_Purple] " << '\n'
4084 << " [0.95 color Light_Purple] " << '\n'
4085 << " [1.00 color White] " << '\n'
4086 << "} } }" << '\n'
4087 << '\n';
4088 }
4089
4090 if (!flags.bicubic_patch)
4091 {
4092 // start of mesh header
4093 out << '\n' << "mesh {" << '\n';
4094 }
4095
4096 // loop over all patches
4097 for (const auto &patch : patches)
4098 {
4099 const unsigned int n_subdivisions = patch.n_subdivisions;
4100 const unsigned int n = n_subdivisions + 1;
4101 const unsigned int d1 = 1;
4102 const unsigned int d2 = n;
4103
4104 Assert((patch.data.n_rows() == n_data_sets &&
4105 !patch.points_are_available) ||
4106 (patch.data.n_rows() == n_data_sets + spacedim &&
4107 patch.points_are_available),
4109 (n_data_sets + spacedim) :
4110 n_data_sets,
4111 patch.data.n_rows()));
4112 Assert(patch.data.n_cols() == Utilities::fixed_power<dim>(n),
4113 ExcInvalidDatasetSize(patch.data.n_cols(),
4114 n_subdivisions + 1));
4115
4116
4117 std::vector<Point<spacedim>> ver(n * n);
4118
4119 for (unsigned int i2 = 0; i2 < n; ++i2)
4120 for (unsigned int i1 = 0; i1 < n; ++i1)
4121 {
4122 // compute coordinates for this patch point, storing in ver
4123 ver[i1 * d1 + i2 * d2] =
4124 get_equispaced_location(patch, {i1, i2}, n_subdivisions);
4125 }
4126
4127
4128 if (!flags.bicubic_patch)
4129 {
4130 // approximate normal vectors in patch
4131 std::vector<Point<3>> nrml;
4132 // only if smooth triangles are used
4133 if (flags.smooth)
4134 {
4135 nrml.resize(n * n);
4136 // These are difference quotients of the surface
4137 // mapping. We take them symmetric inside the
4138 // patch and one-sided at the edges
4139 Point<3> h1, h2;
4140 // Now compute normals in every point
4141 for (unsigned int i = 0; i < n; ++i)
4142 for (unsigned int j = 0; j < n; ++j)
4143 {
4144 const unsigned int il = (i == 0) ? i : (i - 1);
4145 const unsigned int ir =
4146 (i == n_subdivisions) ? i : (i + 1);
4147 const unsigned int jl = (j == 0) ? j : (j - 1);
4148 const unsigned int jr =
4149 (j == n_subdivisions) ? j : (j + 1);
4150
4151 h1[0] =
4152 ver[ir * d1 + j * d2][0] - ver[il * d1 + j * d2][0];
4153 h1[1] = patch.data(0, ir * d1 + j * d2) -
4154 patch.data(0, il * d1 + j * d2);
4155 h1[2] =
4156 ver[ir * d1 + j * d2][1] - ver[il * d1 + j * d2][1];
4157
4158 h2[0] =
4159 ver[i * d1 + jr * d2][0] - ver[i * d1 + jl * d2][0];
4160 h2[1] = patch.data(0, i * d1 + jr * d2) -
4161 patch.data(0, i * d1 + jl * d2);
4162 h2[2] =
4163 ver[i * d1 + jr * d2][1] - ver[i * d1 + jl * d2][1];
4164
4165 nrml[i * d1 + j * d2][0] =
4166 h1[1] * h2[2] - h1[2] * h2[1];
4167 nrml[i * d1 + j * d2][1] =
4168 h1[2] * h2[0] - h1[0] * h2[2];
4169 nrml[i * d1 + j * d2][2] =
4170 h1[0] * h2[1] - h1[1] * h2[0];
4171
4172 // normalize Vector
4173 double norm = std::hypot(nrml[i * d1 + j * d2][0],
4174 nrml[i * d1 + j * d2][1],
4175 nrml[i * d1 + j * d2][2]);
4176
4177 if (nrml[i * d1 + j * d2][1] < 0)
4178 norm *= -1.;
4179
4180 for (unsigned int k = 0; k < 3; ++k)
4181 nrml[i * d1 + j * d2][k] /= norm;
4182 }
4183 }
4184
4185 // setting up triangles
4186 for (unsigned int i = 0; i < n_subdivisions; ++i)
4187 for (unsigned int j = 0; j < n_subdivisions; ++j)
4188 {
4189 // down/left vertex of triangle
4190 const int dl = i * d1 + j * d2;
4191 if (flags.smooth)
4192 {
4193 // writing smooth_triangles
4194
4195 // down/right triangle
4196 out << "smooth_triangle {" << '\n'
4197 << "\t<" << ver[dl][0] << "," << patch.data(0, dl)
4198 << "," << ver[dl][1] << ">, <" << nrml[dl][0]
4199 << ", " << nrml[dl][1] << ", " << nrml[dl][2]
4200 << ">," << '\n';
4201 out << " \t<" << ver[dl + d1][0] << ","
4202 << patch.data(0, dl + d1) << "," << ver[dl + d1][1]
4203 << ">, <" << nrml[dl + d1][0] << ", "
4204 << nrml[dl + d1][1] << ", " << nrml[dl + d1][2]
4205 << ">," << '\n';
4206 out << "\t<" << ver[dl + d1 + d2][0] << ","
4207 << patch.data(0, dl + d1 + d2) << ","
4208 << ver[dl + d1 + d2][1] << ">, <"
4209 << nrml[dl + d1 + d2][0] << ", "
4210 << nrml[dl + d1 + d2][1] << ", "
4211 << nrml[dl + d1 + d2][2] << ">}" << '\n';
4212
4213 // upper/left triangle
4214 out << "smooth_triangle {" << '\n'
4215 << "\t<" << ver[dl][0] << "," << patch.data(0, dl)
4216 << "," << ver[dl][1] << ">, <" << nrml[dl][0]
4217 << ", " << nrml[dl][1] << ", " << nrml[dl][2]
4218 << ">," << '\n';
4219 out << "\t<" << ver[dl + d1 + d2][0] << ","
4220 << patch.data(0, dl + d1 + d2) << ","
4221 << ver[dl + d1 + d2][1] << ">, <"
4222 << nrml[dl + d1 + d2][0] << ", "
4223 << nrml[dl + d1 + d2][1] << ", "
4224 << nrml[dl + d1 + d2][2] << ">," << '\n';
4225 out << "\t<" << ver[dl + d2][0] << ","
4226 << patch.data(0, dl + d2) << "," << ver[dl + d2][1]
4227 << ">, <" << nrml[dl + d2][0] << ", "
4228 << nrml[dl + d2][1] << ", " << nrml[dl + d2][2]
4229 << ">}" << '\n';
4230 }
4231 else
4232 {
4233 // writing standard triangles down/right triangle
4234 out << "triangle {" << '\n'
4235 << "\t<" << ver[dl][0] << "," << patch.data(0, dl)
4236 << "," << ver[dl][1] << ">," << '\n';
4237 out << "\t<" << ver[dl + d1][0] << ","
4238 << patch.data(0, dl + d1) << "," << ver[dl + d1][1]
4239 << ">," << '\n';
4240 out << "\t<" << ver[dl + d1 + d2][0] << ","
4241 << patch.data(0, dl + d1 + d2) << ","
4242 << ver[dl + d1 + d2][1] << ">}" << '\n';
4243
4244 // upper/left triangle
4245 out << "triangle {" << '\n'
4246 << "\t<" << ver[dl][0] << "," << patch.data(0, dl)
4247 << "," << ver[dl][1] << ">," << '\n';
4248 out << "\t<" << ver[dl + d1 + d2][0] << ","
4249 << patch.data(0, dl + d1 + d2) << ","
4250 << ver[dl + d1 + d2][1] << ">," << '\n';
4251 out << "\t<" << ver[dl + d2][0] << ","
4252 << patch.data(0, dl + d2) << "," << ver[dl + d2][1]
4253 << ">}" << '\n';
4254 }
4255 }
4256 }
4257 else
4258 {
4259 // writing bicubic_patch
4260 Assert(n_subdivisions == 3,
4261 ExcDimensionMismatch(n_subdivisions, 3));
4262 out << '\n'
4263 << "bicubic_patch {" << '\n'
4264 << " type 0" << '\n'
4265 << " flatness 0" << '\n'
4266 << " u_steps 0" << '\n'
4267 << " v_steps 0" << '\n';
4268 for (int i = 0; i < 16; ++i)
4269 {
4270 out << "\t<" << ver[i][0] << "," << patch.data(0, i) << ","
4271 << ver[i][1] << ">";
4272 if (i != 15)
4273 out << ",";
4274 out << '\n';
4275 }
4276 out << " texture {Tex}" << '\n' << "}" << '\n';
4277 }
4278 }
4279
4280 if (!flags.bicubic_patch)
4281 {
4282 // the end of the mesh
4283 out << " texture {Tex}" << '\n' << "}" << '\n' << '\n';
4284 }
4285
4286 // make sure everything now gets to disk
4287 out.flush();
4288
4289 AssertThrow(out.fail() == false, ExcIO());
4290 }
4291 } // namespace
4292
4293
4294
4295 template <int dim, int spacedim>
4296 void
4298 const std::vector<Patch<dim, spacedim>> &patches,
4299 const std::vector<std::string> &data_names,
4300 const std::vector<
4301 std::tuple<unsigned int,
4302 unsigned int,
4303 std::string,
4305 const PovrayFlags &flags,
4306 std::ostream &out)
4307 {
4308 do_write_povray(patches, data_names, flags, out);
4309 }
4310
4311
4312
4313 template <int dim, int spacedim>
4314 void
4316 const std::vector<Patch<dim, spacedim>> & /*patches*/,
4317 const std::vector<std::string> & /*data_names*/,
4318 const std::vector<
4319 std::tuple<unsigned int,
4320 unsigned int,
4321 std::string,
4323 const EpsFlags & /*flags*/,
4324 std::ostream & /*out*/)
4325 {
4326 // not implemented, see the documentation of the function
4327 AssertThrow(dim == 2, ExcNotImplemented());
4328 }
4329
4330
4331 template <int spacedim>
4332 void
4334 const std::vector<Patch<2, spacedim>> &patches,
4335 const std::vector<std::string> & /*data_names*/,
4336 const std::vector<
4337 std::tuple<unsigned int,
4338 unsigned int,
4339 std::string,
4341 const EpsFlags &flags,
4342 std::ostream &out)
4343 {
4344 AssertThrow(out.fail() == false, ExcIO());
4345
4346#ifndef DEAL_II_WITH_MPI
4347 // verify that there are indeed patches to be written out. most of the
4348 // times, people just forget to call build_patches when there are no
4349 // patches, so a warning is in order. that said, the assertion is disabled
4350 // if we support MPI since then it can happen that on the coarsest mesh, a
4351 // processor simply has no cells it actually owns, and in that case it is
4352 // legit if there are no patches
4353 Assert(patches.size() > 0, ExcNoPatches());
4354#else
4355 if (patches.empty())
4356 return;
4357#endif
4358
4359 // set up an array of cells to be written later. this array holds the cells
4360 // of all the patches as projected to the plane perpendicular to the line of
4361 // sight.
4362 //
4363 // note that they are kept sorted by the set, where we chose the value of
4364 // the center point of the cell along the line of sight as value for sorting
4365 std::multiset<EpsCell2d> cells;
4366
4367 // two variables in which we will store the minimum and maximum values of
4368 // the field to be used for colorization
4369 float min_color_value = std::numeric_limits<float>::max();
4370 float max_color_value = std::numeric_limits<float>::min();
4371
4372 // Array for z-coordinates of points. The elevation determined by a function
4373 // if spacedim=2 or the z-coordinate of the grid point if spacedim=3
4374 double heights[4] = {0, 0, 0, 0};
4375
4376 // compute the cells for output and enter them into the set above note that
4377 // since dim==2, we have exactly four vertices per patch and per cell
4378 for (const auto &patch : patches)
4379 {
4380 const unsigned int n_subdivisions = patch.n_subdivisions;
4381 const unsigned int n = n_subdivisions + 1;
4382 const unsigned int d1 = 1;
4383 const unsigned int d2 = n;
4384
4385 for (unsigned int i2 = 0; i2 < n_subdivisions; ++i2)
4386 for (unsigned int i1 = 0; i1 < n_subdivisions; ++i1)
4387 {
4388 Point<spacedim> points[4];
4389 points[0] =
4390 get_equispaced_location(patch, {i1, i2}, n_subdivisions);
4391 points[1] =
4392 get_equispaced_location(patch, {i1 + 1, i2}, n_subdivisions);
4393 points[2] =
4394 get_equispaced_location(patch, {i1, i2 + 1}, n_subdivisions);
4395 points[3] = get_equispaced_location(patch,
4396 {i1 + 1, i2 + 1},
4397 n_subdivisions);
4398
4399 switch (spacedim)
4400 {
4401 case 2:
4402 Assert((flags.height_vector < patch.data.n_rows()) ||
4403 patch.data.n_rows() == 0,
4405 0,
4406 patch.data.n_rows()));
4407 heights[0] =
4408 patch.data.n_rows() != 0 ?
4409 patch.data(flags.height_vector, i1 * d1 + i2 * d2) *
4410 flags.z_scaling :
4411 0;
4412 heights[1] = patch.data.n_rows() != 0 ?
4413 patch.data(flags.height_vector,
4414 (i1 + 1) * d1 + i2 * d2) *
4415 flags.z_scaling :
4416 0;
4417 heights[2] = patch.data.n_rows() != 0 ?
4418 patch.data(flags.height_vector,
4419 i1 * d1 + (i2 + 1) * d2) *
4420 flags.z_scaling :
4421 0;
4422 heights[3] = patch.data.n_rows() != 0 ?
4423 patch.data(flags.height_vector,
4424 (i1 + 1) * d1 + (i2 + 1) * d2) *
4425 flags.z_scaling :
4426 0;
4427
4428 break;
4429 case 3:
4430 // Copy z-coordinates into the height vector
4431 for (unsigned int i = 0; i < 4; ++i)
4432 heights[i] = points[i][2];
4433 break;
4434 default:
4436 }
4437
4438
4439 // now compute the projection of the bilinear cell given by the
4440 // four vertices and their heights and write them to a proper cell
4441 // object. note that we only need the first two components of the
4442 // projected position for output, but we need the value along the
4443 // line of sight for sorting the cells for back-to- front-output
4444 //
4445 // this computation was first written by Stefan Nauber. please
4446 // no-one ask me why it works that way (or may be not), especially
4447 // not about the angles and the sign of the height field, I don't
4448 // know it.
4449 EpsCell2d eps_cell;
4450 const double pi = numbers::PI;
4451 const double cx =
4452 -std::cos(pi - flags.azimut_angle * 2 * pi / 360.),
4453 cz = -std::cos(flags.turn_angle * 2 * pi / 360.),
4454 sx =
4455 std::sin(pi - flags.azimut_angle * 2 * pi / 360.),
4456 sz = std::sin(flags.turn_angle * 2 * pi / 360.);
4457 for (unsigned int vertex = 0; vertex < 4; ++vertex)
4458 {
4459 const double x = points[vertex][0], y = points[vertex][1],
4460 z = -heights[vertex];
4461
4462 eps_cell.vertices[vertex][0] = -cz * x + sz * y;
4463 eps_cell.vertices[vertex][1] =
4464 -cx * sz * x - cx * cz * y - sx * z;
4465
4466 // ( 1 0 0 )
4467 // D1 = ( 0 cx -sx )
4468 // ( 0 sx cx )
4469
4470 // ( cy 0 sy )
4471 // Dy = ( 0 1 0 )
4472 // (-sy 0 cy )
4473
4474 // ( cz -sz 0 )
4475 // Dz = ( sz cz 0 )
4476 // ( 0 0 1 )
4477
4478 // ( cz -sz 0 )( 1 0 0 )(x) (
4479 // cz*x-sz*(cx*y-sx*z)+0*(sx*y+cx*z) )
4480 // Dxz = ( sz cz 0 )( 0 cx -sx )(y) = (
4481 // sz*x+cz*(cx*y-sx*z)+0*(sx*y+cx*z) )
4482 // ( 0 0 1 )( 0 sx cx )(z) ( 0*x+
4483 // *(cx*y-sx*z)+1*(sx*y+cx*z) )
4484 }
4485
4486 // compute coordinates of center of cell
4487 const Point<spacedim> center_point =
4488 (points[0] + points[1] + points[2] + points[3]) / 4;
4489 const double center_height =
4490 -(heights[0] + heights[1] + heights[2] + heights[3]) / 4;
4491
4492 // compute the depth into the picture
4493 eps_cell.depth = -sx * sz * center_point[0] -
4494 sx * cz * center_point[1] + cx * center_height;
4495
4496 if (flags.draw_cells && flags.shade_cells)
4497 {
4498 Assert((flags.color_vector < patch.data.n_rows()) ||
4499 patch.data.n_rows() == 0,
4501 0,
4502 patch.data.n_rows()));
4503 const double color_values[4] = {
4504 patch.data.n_rows() != 0 ?
4505 patch.data(flags.color_vector, i1 * d1 + i2 * d2) :
4506 1,
4507
4508 patch.data.n_rows() != 0 ?
4509 patch.data(flags.color_vector, (i1 + 1) * d1 + i2 * d2) :
4510 1,
4511
4512 patch.data.n_rows() != 0 ?
4513 patch.data(flags.color_vector, i1 * d1 + (i2 + 1) * d2) :
4514 1,
4515
4516 patch.data.n_rows() != 0 ?
4517 patch.data(flags.color_vector,
4518 (i1 + 1) * d1 + (i2 + 1) * d2) :
4519 1};
4520
4521 // set color value to average of the value at the vertices
4522 eps_cell.color_value = (color_values[0] + color_values[1] +
4523 color_values[3] + color_values[2]) /
4524 4;
4525
4526 // update bounds of color field
4527 min_color_value =
4528 std::min(min_color_value, eps_cell.color_value);
4529 max_color_value =
4530 std::max(max_color_value, eps_cell.color_value);
4531 }
4532
4533 // finally add this cell
4534 cells.insert(eps_cell);
4535 }
4536 }
4537
4538 // find out minimum and maximum x and y coordinates to compute offsets and
4539 // scaling factors
4540 double x_min = cells.begin()->vertices[0][0];
4541 double x_max = x_min;
4542 double y_min = cells.begin()->vertices[0][1];
4543 double y_max = y_min;
4544
4545 for (const auto &cell : cells)
4546 for (const auto &vertex : cell.vertices)
4547 {
4548 x_min = std::min(x_min, vertex[0]);
4549 x_max = std::max(x_max, vertex[0]);
4550 y_min = std::min(y_min, vertex[1]);
4551 y_max = std::max(y_max, vertex[1]);
4552 }
4553
4554 // scale in x-direction such that in the output 0 <= x <= 300. don't scale
4555 // in y-direction to preserve the shape of the triangulation
4556 const double scale =
4557 (flags.size /
4558 (flags.size_type == EpsFlags::width ? x_max - x_min : y_min - y_max));
4559
4560 const Point<2> offset(x_min, y_min);
4561
4562
4563 // now write preamble
4564 {
4565 out << "%!PS-Adobe-2.0 EPSF-1.2" << '\n'
4566 << "%%Title: deal.II Output" << '\n'
4567 << "%%Creator: the deal.II library" << '\n'
4568 << "%%Creation Date: " << Utilities::System::get_date() << " - "
4569 << Utilities::System::get_time() << '\n'
4570 << "%%BoundingBox: "
4571 // lower left corner
4572 << "0 0 "
4573 // upper right corner
4574 << static_cast<unsigned int>((x_max - x_min) * scale + 0.5) << ' '
4575 << static_cast<unsigned int>((y_max - y_min) * scale + 0.5) << '\n';
4576
4577 // define some abbreviations to keep the output small:
4578 // m=move turtle to
4579 // l=define a line
4580 // s=set rgb color
4581 // sg=set gray value
4582 // lx=close the line and plot the line
4583 // lf=close the line and fill the interior
4584 out << "/m {moveto} bind def" << '\n'
4585 << "/l {lineto} bind def" << '\n'
4586 << "/s {setrgbcolor} bind def" << '\n'
4587 << "/sg {setgray} bind def" << '\n'
4588 << "/lx {lineto closepath stroke} bind def" << '\n'
4589 << "/lf {lineto closepath fill} bind def" << '\n';
4590
4591 out << "%%EndProlog" << '\n' << '\n';
4592 // set fine lines
4593 out << flags.line_width << " setlinewidth" << '\n';
4594 }
4595
4596 // check if min and max values for the color are actually different. If
4597 // that is not the case (such things happen, for example, in the very first
4598 // time step of a time dependent problem, if the initial values are zero),
4599 // all values are equal, and then we can draw everything in an arbitrary
4600 // color. Thus, change one of the two values arbitrarily
4601 if (max_color_value == min_color_value)
4602 max_color_value = min_color_value + 1;
4603
4604 // now we've got all the information we need. write the cells. note: due to
4605 // the ordering, we traverse the list of cells back-to-front
4606 for (const auto &cell : cells)
4607 {
4608 if (flags.draw_cells)
4609 {
4610 if (flags.shade_cells)
4611 {
4612 const EpsFlags::RgbValues rgb_values =
4613 (*flags.color_function)(cell.color_value,
4614 min_color_value,
4615 max_color_value);
4616
4617 // write out color
4618 if (rgb_values.is_grey())
4619 out << rgb_values.red << " sg ";
4620 else
4621 out << rgb_values.red << ' ' << rgb_values.green << ' '
4622 << rgb_values.blue << " s ";
4623 }
4624 else
4625 out << "1 sg ";
4626
4627 out << (cell.vertices[0] - offset) * scale << " m "
4628 << (cell.vertices[1] - offset) * scale << " l "
4629 << (cell.vertices[3] - offset) * scale << " l "
4630 << (cell.vertices[2] - offset) * scale << " lf" << '\n';
4631 }
4632
4633 if (flags.draw_mesh)
4634 out << "0 sg " // draw lines in black
4635 << (cell.vertices[0] - offset) * scale << " m "
4636 << (cell.vertices[1] - offset) * scale << " l "
4637 << (cell.vertices[3] - offset) * scale << " l "
4638 << (cell.vertices[2] - offset) * scale << " lx" << '\n';
4639 }
4640 out << "showpage" << '\n';
4641
4642 out.flush();
4643
4644 AssertThrow(out.fail() == false, ExcIO());
4645 }
4646
4647
4648
4649 template <int dim, int spacedim>
4650 void
4652 const std::vector<Patch<dim, spacedim>> &patches,
4653 const std::vector<std::string> &data_names,
4654 const std::vector<
4655 std::tuple<unsigned int,
4656 unsigned int,
4657 std::string,
4659 const GmvFlags &flags,
4660 std::ostream &out)
4661 {
4662 // The gmv format does not support cells that only consist of a single
4663 // point. It does support the output of point data using the keyword
4664 // 'tracers' instead of 'nodes' and 'cells', but this output format is
4665 // currently not implemented.
4666 AssertThrow(dim > 0, ExcNotImplemented());
4667
4668 Assert(dim <= 3, ExcNotImplemented());
4669 AssertThrow(out.fail() == false, ExcIO());
4670
4671#ifndef DEAL_II_WITH_MPI
4672 // verify that there are indeed patches to be written out. most of the
4673 // times, people just forget to call build_patches when there are no
4674 // patches, so a warning is in order. that said, the assertion is disabled
4675 // if we support MPI since then it can happen that on the coarsest mesh, a
4676 // processor simply has no cells it actually owns, and in that case it is
4677 // legit if there are no patches
4678 Assert(patches.size() > 0, ExcNoPatches());
4679#else
4680 if (patches.empty())
4681 return;
4682#endif
4683
4684 GmvStream gmv_out(out, flags);
4685 const unsigned int n_data_sets = data_names.size();
4686 // check against # of data sets in first patch. checks against all other
4687 // patches are made in write_gmv_reorder_data_vectors
4688 Assert((patches[0].data.n_rows() == n_data_sets &&
4689 !patches[0].points_are_available) ||
4690 (patches[0].data.n_rows() == n_data_sets + spacedim &&
4691 patches[0].points_are_available),
4692 ExcDimensionMismatch(patches[0].points_are_available ?
4693 (n_data_sets + spacedim) :
4694 n_data_sets,
4695 patches[0].data.n_rows()));
4696
4697 //---------------------
4698 // preamble
4699 out << "gmvinput ascii" << '\n' << '\n';
4700
4701 // first count the number of cells and cells for later use
4702 unsigned int n_nodes;
4703 unsigned int n_cells;
4704 std::tie(n_nodes, n_cells) = count_nodes_and_cells(patches);
4705
4706 // For the format we write here, we need to write all node values relating
4707 // to one variable at a time. We could in principle do this by looping
4708 // over all patches and extracting the values corresponding to the one
4709 // variable we're dealing with right now, and then start the process over
4710 // for the next variable with another loop over all patches.
4711 //
4712 // An easier way is to create a global table that for each variable
4713 // lists all values. This copying of data vectors can be done in the
4714 // background while we're already working on vertices and cells,
4715 // so do this on a separate task and when wanting to write out the
4716 // data, we wait for that task to finish.
4718 create_global_data_table_task = Threads::new_task(
4719 [&patches]() { return create_global_data_table(patches); });
4720
4721 //-----------------------------
4722 // first make up a list of used vertices along with their coordinates
4723 //
4724 // note that we have to print 3 dimensions
4725 out << "nodes " << n_nodes << '\n';
4726 for (unsigned int d = 0; d < spacedim; ++d)
4727 {
4728 gmv_out.selected_component = d;
4729 write_nodes(patches, gmv_out);
4730 out << '\n';
4731 }
4732 gmv_out.selected_component = numbers::invalid_unsigned_int;
4733
4734 for (unsigned int d = spacedim; d < 3; ++d)
4735 {
4736 for (unsigned int i = 0; i < n_nodes; ++i)
4737 out << "0 ";
4738 out << '\n';
4739 }
4740
4741 //-------------------------------
4742 // now for the cells. note that vertices are counted from 1 onwards
4743 out << "cells " << n_cells << '\n';
4744 write_cells(patches, gmv_out);
4745
4746 //-------------------------------------
4747 // data output.
4748 out << "variable" << '\n';
4749
4750 // Wait for the reordering to be done and retrieve the reordered data:
4751 const Table<2, double> data_vectors =
4752 std::move(*create_global_data_table_task.return_value());
4753
4754 // then write data. the '1' means: node data (as opposed to cell data, which
4755 // we do not support explicitly here)
4756 for (unsigned int data_set = 0; data_set < n_data_sets; ++data_set)
4757 {
4758 out << data_names[data_set] << " 1" << '\n';
4759 std::copy(data_vectors[data_set].begin(),
4760 data_vectors[data_set].end(),
4761 std::ostream_iterator<double>(out, " "));
4762 out << '\n' << '\n';
4763 }
4764
4765
4766
4767 // end of variable section
4768 out << "endvars" << '\n';
4769
4770 // end of output
4771 out << "endgmv" << '\n';
4772
4773 // make sure everything now gets to disk
4774 out.flush();
4775
4776 // assert the stream is still ok
4777 AssertThrow(out.fail() == false, ExcIO());
4778 }
4779
4780
4781
4782 template <int dim, int spacedim>
4783 void
4785 const std::vector<Patch<dim, spacedim>> &patches,
4786 const std::vector<std::string> &data_names,
4787 const std::vector<
4788 std::tuple<unsigned int,
4789 unsigned int,
4790 std::string,
4792 const TecplotFlags &flags,
4793 std::ostream &out)
4794 {
4795 AssertThrow(out.fail() == false, ExcIO());
4796
4797 // The FEBLOCK or FEPOINT formats of tecplot only allows full elements (e.g.
4798 // triangles), not single points. Other tecplot format allow point output,
4799 // but they are currently not implemented.
4800 AssertThrow(dim > 0, ExcNotImplemented());
4801
4802#ifndef DEAL_II_WITH_MPI
4803 // verify that there are indeed patches to be written out. most of the
4804 // times, people just forget to call build_patches when there are no
4805 // patches, so a warning is in order. that said, the assertion is disabled
4806 // if we support MPI since then it can happen that on the coarsest mesh, a
4807 // processor simply has no cells it actually owns, and in that case it is
4808 // legit if there are no patches
4809 Assert(patches.size() > 0, ExcNoPatches());
4810#else
4811 if (patches.empty())
4812 return;
4813#endif
4814
4815 TecplotStream tecplot_out(out, flags);
4816
4817 const unsigned int n_data_sets = data_names.size();
4818 // check against # of data sets in first patch. checks against all other
4819 // patches are made in write_gmv_reorder_data_vectors
4820 Assert((patches[0].data.n_rows() == n_data_sets &&
4821 !patches[0].points_are_available) ||
4822 (patches[0].data.n_rows() == n_data_sets + spacedim &&
4823 patches[0].points_are_available),
4824 ExcDimensionMismatch(patches[0].points_are_available ?
4825 (n_data_sets + spacedim) :
4826 n_data_sets,
4827 patches[0].data.n_rows()));
4828
4829 // first count the number of cells and cells for later use
4830 unsigned int n_nodes;
4831 unsigned int n_cells;
4832 std::tie(n_nodes, n_cells) = count_nodes_and_cells(patches);
4833
4834 //---------
4835 // preamble
4836 {
4837 out
4838 << "# This file was generated by the deal.II library." << '\n'
4839 << "# Date = " << Utilities::System::get_date() << '\n'
4840 << "# Time = " << Utilities::System::get_time() << '\n'
4841 << "#" << '\n'
4842 << "# For a description of the Tecplot format see the Tecplot documentation."
4843 << '\n'
4844 << "#" << '\n';
4845
4846
4847 out << "Variables=";
4848
4849 switch (spacedim)
4850 {
4851 case 1:
4852 out << "\"x\"";
4853 break;
4854 case 2:
4855 out << "\"x\", \"y\"";
4856 break;
4857 case 3:
4858 out << "\"x\", \"y\", \"z\"";
4859 break;
4860 default:
4862 }
4863
4864 for (unsigned int data_set = 0; data_set < n_data_sets; ++data_set)
4865 out << ", \"" << data_names[data_set] << "\"";
4866
4867 out << '\n';
4868
4869 out << "zone ";
4870 if (flags.zone_name)
4871 out << "t=\"" << flags.zone_name << "\" ";
4872
4873 if (flags.solution_time >= 0.0)
4874 out << "strandid=1, solutiontime=" << flags.solution_time << ", ";
4875
4876 out << "f=feblock, n=" << n_nodes << ", e=" << n_cells
4877 << ", et=" << tecplot_cell_type[dim] << '\n';
4878 }
4879
4880
4881 // For the format we write here, we need to write all node values relating
4882 // to one variable at a time. We could in principle do this by looping
4883 // over all patches and extracting the values corresponding to the one
4884 // variable we're dealing with right now, and then start the process over
4885 // for the next variable with another loop over all patches.
4886 //
4887 // An easier way is to create a global table that for each variable
4888 // lists all values. This copying of data vectors can be done in the
4889 // background while we're already working on vertices and cells,
4890 // so do this on a separate task and when wanting to write out the
4891 // data, we wait for that task to finish.
4893 create_global_data_table_task = Threads::new_task(
4894 [&patches]() { return create_global_data_table(patches); });
4895
4896 //-----------------------------
4897 // first make up a list of used vertices along with their coordinates
4898
4899
4900 for (unsigned int d = 0; d < spacedim; ++d)
4901 {
4902 tecplot_out.selected_component = d;
4903 write_nodes(patches, tecplot_out);
4904 out << '\n';
4905 }
4906
4907
4908 //-------------------------------------
4909 // data output.
4910 //
4911 // Wait for the reordering to be done and retrieve the reordered data:
4912 const Table<2, double> data_vectors =
4913 std::move(*create_global_data_table_task.return_value());
4914
4915 // then write data.
4916 for (unsigned int data_set = 0; data_set < n_data_sets; ++data_set)
4917 {
4918 std::copy(data_vectors[data_set].begin(),
4919 data_vectors[data_set].end(),
4920 std::ostream_iterator<double>(out, "\n"));
4921 out << '\n';
4922 }
4923
4924 write_cells(patches, tecplot_out);
4925
4926 // make sure everything now gets to disk
4927 out.flush();
4928
4929 // assert the stream is still ok
4930 AssertThrow(out.fail() == false, ExcIO());
4931 }
4932
4933
4934
4935 template <int dim, int spacedim>
4936 void
4938 const std::vector<Patch<dim, spacedim>> &patches,
4939 const std::vector<std::string> &data_names,
4940 const std::vector<
4941 std::tuple<unsigned int,
4942 unsigned int,
4943 std::string,
4945 &nonscalar_data_ranges,
4946 const VtkFlags &flags,
4947 std::ostream &out)
4948 {
4949 AssertThrow(out.fail() == false, ExcIO());
4950
4951#ifndef DEAL_II_WITH_MPI
4952 // verify that there are indeed patches to be written out. most of the
4953 // times, people just forget to call build_patches when there are no
4954 // patches, so a warning is in order. that said, the assertion is disabled
4955 // if we support MPI since then it can happen that on the coarsest mesh, a
4956 // processor simply has no cells it actually owns, and in that case it is
4957 // legit if there are no patches
4958 Assert(patches.size() > 0, ExcNoPatches());
4959#else
4960 if (patches.empty())
4961 return;
4962#endif
4963
4964 VtkStream vtk_out(out, flags);
4965
4966 const unsigned int n_data_sets = data_names.size();
4967 // check against # of data sets in first patch.
4968 if (patches[0].points_are_available)
4969 {
4970 AssertDimension(n_data_sets + spacedim, patches[0].data.n_rows());
4971 }
4972 else
4973 {
4974 AssertDimension(n_data_sets, patches[0].data.n_rows());
4975 }
4976
4977 //---------------------
4978 // preamble
4979 {
4980 out << "# vtk DataFile Version 3.0" << '\n'
4981 << "#This file was generated by the deal.II library";
4982 if (flags.print_date_and_time)
4983 {
4984 out << " on " << Utilities::System::get_date() << " at "
4986 }
4987 else
4988 out << '.';
4989 out << '\n' << "ASCII" << '\n';
4990 // now output the data header
4991 out << "DATASET UNSTRUCTURED_GRID\n" << '\n';
4992 }
4993
4994 // if desired, output time and cycle of the simulation, following the
4995 // instructions at
4996 // http://www.visitusers.org/index.php?title=Time_and_Cycle_in_VTK_files
4997 {
4998 const unsigned int n_metadata =
4999 ((flags.cycle != std::numeric_limits<unsigned int>::min() ? 1 : 0) +
5000 (flags.time != std::numeric_limits<double>::min() ? 1 : 0));
5001 if (n_metadata > 0)
5002 {
5003 out << "FIELD FieldData " << n_metadata << '\n';
5004
5005 if (flags.cycle != std::numeric_limits<unsigned int>::min())
5006 {
5007 out << "CYCLE 1 1 int\n" << flags.cycle << '\n';
5008 }
5009 if (flags.time != std::numeric_limits<double>::min())
5010 {
5011 out << "TIME 1 1 double\n" << flags.time << '\n';
5012 }
5013 }
5014 }
5015
5016 // first count the number of cells and cells for later use
5017 unsigned int n_nodes;
5018 unsigned int n_cells;
5019 unsigned int n_points_and_n_cells;
5020 std::tie(n_nodes, n_cells, n_points_and_n_cells) =
5021 count_nodes_and_cells_and_points(patches, flags.write_higher_order_cells);
5022
5023 // For the format we write here, we need to write all node values relating
5024 // to one variable at a time. We could in principle do this by looping
5025 // over all patches and extracting the values corresponding to the one
5026 // variable we're dealing with right now, and then start the process over
5027 // for the next variable with another loop over all patches.
5028 //
5029 // An easier way is to create a global table that for each variable
5030 // lists all values. This copying of data vectors can be done in the
5031 // background while we're already working on vertices and cells,
5032 // so do this on a separate task and when wanting to write out the
5033 // data, we wait for that task to finish.
5035 create_global_data_table_task = Threads::new_task(
5036 [&patches]() { return create_global_data_table(patches); });
5037
5038 //-----------------------------
5039 // first make up a list of used vertices along with their coordinates
5040 //
5041 // note that we have to print d=1..3 dimensions
5042 out << "POINTS " << n_nodes << " double" << '\n';
5043 write_nodes(patches, vtk_out);
5044 out << '\n';
5045 //-------------------------------
5046 // now for the cells
5047 out << "CELLS " << n_cells << ' ' << n_points_and_n_cells << '\n';
5048 if (flags.write_higher_order_cells)
5049 write_high_order_cells(patches, vtk_out, /* legacy_format = */ true);
5050 else
5051 write_cells(patches, vtk_out);
5052 out << '\n';
5053 // next output the types of the cells. since all cells are the same, this is
5054 // simple
5055 out << "CELL_TYPES " << n_cells << '\n';
5056
5057 // need to distinguish between linear cells, simplex cells (linear or
5058 // quadratic), and high order cells
5059 for (const auto &patch : patches)
5060 {
5061 const auto vtk_cell_id =
5062 extract_vtk_patch_info(patch, flags.write_higher_order_cells);
5063
5064 for (unsigned int i = 0; i < vtk_cell_id[1]; ++i)
5065 out << ' ' << vtk_cell_id[0];
5066 }
5067
5068 out << '\n';
5069 //-------------------------------------
5070 // data output.
5071
5072 // Wait for the reordering to be done and retrieve the reordered data:
5073 const Table<2, double> data_vectors =
5074 std::move(*create_global_data_table_task.return_value());
5075
5076 // then write data. the 'POINT_DATA' means: node data (as opposed to cell
5077 // data, which we do not support explicitly here). all following data sets
5078 // are point data
5079 out << "POINT_DATA " << n_nodes << '\n';
5080
5081 // when writing, first write out all vector data, then handle the scalar
5082 // data sets that have been left over
5083 std::vector<bool> data_set_written(n_data_sets, false);
5084 for (const auto &nonscalar_data_range : nonscalar_data_ranges)
5085 {
5086 AssertThrow(std::get<3>(nonscalar_data_range) !=
5088 ExcMessage(
5089 "The VTK writer does not currently support outputting "
5090 "tensor data. Use the VTU writer instead."));
5091
5092 AssertThrow(std::get<1>(nonscalar_data_range) >=
5093 std::get<0>(nonscalar_data_range),
5094 ExcLowerRange(std::get<1>(nonscalar_data_range),
5095 std::get<0>(nonscalar_data_range)));
5096 AssertThrow(std::get<1>(nonscalar_data_range) < n_data_sets,
5097 ExcIndexRange(std::get<1>(nonscalar_data_range),
5098 0,
5099 n_data_sets));
5100 AssertThrow(std::get<1>(nonscalar_data_range) + 1 -
5101 std::get<0>(nonscalar_data_range) <=
5102 3,
5103 ExcMessage(
5104 "Can't declare a vector with more than 3 components "
5105 "in VTK"));
5106
5107 // mark these components as already written:
5108 for (unsigned int i = std::get<0>(nonscalar_data_range);
5109 i <= std::get<1>(nonscalar_data_range);
5110 ++i)
5111 data_set_written[i] = true;
5112
5113 // write the header. concatenate all the component names with double
5114 // underscores unless a vector name has been specified
5115 out << "VECTORS ";
5116
5117 if (!std::get<2>(nonscalar_data_range).empty())
5118 out << std::get<2>(nonscalar_data_range);
5119 else
5120 {
5121 for (unsigned int i = std::get<0>(nonscalar_data_range);
5122 i < std::get<1>(nonscalar_data_range);
5123 ++i)
5124 out << data_names[i] << "__";
5125 out << data_names[std::get<1>(nonscalar_data_range)];
5126 }
5127
5128 out << " double" << '\n';
5129
5130 // now write data. pad all vectors to have three components
5131 for (unsigned int n = 0; n < n_nodes; ++n)
5132 {
5133 switch (std::get<1>(nonscalar_data_range) -
5134 std::get<0>(nonscalar_data_range))
5135 {
5136 case 0:
5137 out << data_vectors(std::get<0>(nonscalar_data_range), n)
5138 << " 0 0" << '\n';
5139 break;
5140
5141 case 1:
5142 out << data_vectors(std::get<0>(nonscalar_data_range), n)
5143 << ' '
5144 << data_vectors(std::get<0>(nonscalar_data_range) + 1, n)
5145 << " 0" << '\n';
5146 break;
5147 case 2:
5148 out << data_vectors(std::get<0>(nonscalar_data_range), n)
5149 << ' '
5150 << data_vectors(std::get<0>(nonscalar_data_range) + 1, n)
5151 << ' '
5152 << data_vectors(std::get<0>(nonscalar_data_range) + 2, n)
5153 << '\n';
5154 break;
5155
5156 default:
5157 // VTK doesn't support anything else than vectors with 1, 2,
5158 // or 3 components
5160 }
5161 }
5162 }
5163
5164 // now do the left over scalar data sets
5165 for (unsigned int data_set = 0; data_set < n_data_sets; ++data_set)
5166 if (data_set_written[data_set] == false)
5167 {
5168 out << "SCALARS " << data_names[data_set] << " double 1" << '\n'
5169 << "LOOKUP_TABLE default" << '\n';
5170 std::copy(data_vectors[data_set].begin(),
5171 data_vectors[data_set].end(),
5172 std::ostream_iterator<double>(out, " "));
5173 out << '\n';
5174 }
5175
5176 // make sure everything now gets to disk
5177 out.flush();
5178
5179 // assert the stream is still ok
5180 AssertThrow(out.fail() == false, ExcIO());
5181 }
5182
5183
5184 void
5185 write_vtu_header(std::ostream &out, const VtkFlags &flags)
5186 {
5187 AssertThrow(out.fail() == false, ExcIO());
5188 out << "<?xml version=\"1.0\" ?> \n";
5189 out << "<!-- \n";
5190 out << "# vtk DataFile Version 3.0" << '\n'
5191 << "#This file was generated by the deal.II library";
5192 if (flags.print_date_and_time)
5193 {
5194 out << " on " << Utilities::System::get_time() << " at "
5196 }
5197 else
5198 out << '.';
5199 out << "\n-->\n";
5200
5201 if (flags.write_higher_order_cells)
5202 out << "<VTKFile type=\"UnstructuredGrid\" version=\"2.2\"";
5203 else
5204 out << "<VTKFile type=\"UnstructuredGrid\" version=\"0.1\"";
5205 if (deal_ii_with_zlib &&
5207 out << " compressor=\"vtkZLibDataCompressor\"";
5208#ifdef DEAL_II_WORDS_BIGENDIAN
5209 out << " byte_order=\"BigEndian\"";
5210#else
5211 out << " byte_order=\"LittleEndian\"";
5212#endif
5213 out << ">";
5214 out << '\n';
5215 out << "<UnstructuredGrid>";
5216 out << '\n';
5217 }
5218
5219
5220
5221 void
5222 write_vtu_footer(std::ostream &out)
5223 {
5224 AssertThrow(out.fail() == false, ExcIO());
5225 out << " </UnstructuredGrid>\n";
5226 out << "</VTKFile>\n";
5227 }
5228
5229
5230
5231 template <int dim, int spacedim>
5232 void
5234 const std::vector<Patch<dim, spacedim>> &patches,
5235 const std::vector<std::string> &data_names,
5236 const std::vector<
5237 std::tuple<unsigned int,
5238 unsigned int,
5239 std::string,
5241 &nonscalar_data_ranges,
5242 const VtkFlags &flags,
5243 std::ostream &out)
5244 {
5245 write_vtu_header(out, flags);
5246 write_vtu_main(patches, data_names, nonscalar_data_ranges, flags, out);
5247 write_vtu_footer(out);
5248
5249 out << std::flush;
5250 }
5251
5252
5253 template <int dim, int spacedim>
5254 void
5256 const std::vector<Patch<dim, spacedim>> &patches,
5257 const std::vector<std::string> &data_names,
5258 const std::vector<
5259 std::tuple<unsigned int,
5260 unsigned int,
5261 std::string,
5263 &nonscalar_data_ranges,
5264 const VtkFlags &flags,
5265 std::ostream &out)
5266 {
5267 AssertThrow(out.fail() == false, ExcIO());
5268
5269 // If the user provided physical units, make sure that they don't contain
5270 // quote characters as this would make the VTU file invalid XML and
5271 // probably lead to all sorts of difficult error messages. Other than that,
5272 // trust the user that whatever they provide makes sense somehow.
5273 for (const auto &unit : flags.physical_units)
5274 {
5275 (void)unit;
5276 Assert(
5277 unit.second.find('\"') == std::string::npos,
5278 ExcMessage(
5279 "A physical unit you provided, <" + unit.second +
5280 ">, contained a quotation mark character. This is not allowed."));
5281 }
5282
5283#ifndef DEAL_II_WITH_MPI
5284 // verify that there are indeed patches to be written out. most of the
5285 // times, people just forget to call build_patches when there are no
5286 // patches, so a warning is in order. that said, the assertion is disabled
5287 // if we support MPI since then it can happen that on the coarsest mesh, a
5288 // processor simply has no cells it actually owns, and in that case it is
5289 // legit if there are no patches
5290 Assert(patches.size() > 0, ExcNoPatches());
5291#else
5292 if (patches.empty())
5293 {
5294 // we still need to output a valid vtu file, because other CPUs might
5295 // output data. This is the minimal file that is accepted by paraview
5296 // and visit. if we remove the field definitions, visit is complaining.
5297 out << "<Piece NumberOfPoints=\"0\" NumberOfCells=\"0\" >\n"
5298 << "<Cells>\n"
5299 << "<DataArray type=\"UInt8\" Name=\"types\"></DataArray>\n"
5300 << "</Cells>\n"
5301 << " <PointData Scalars=\"scalars\">\n";
5302 std::vector<bool> data_set_written(data_names.size(), false);
5303 for (const auto &nonscalar_data_range : nonscalar_data_ranges)
5304 {
5305 // mark these components as already written:
5306 for (unsigned int i = std::get<0>(nonscalar_data_range);
5307 i <= std::get<1>(nonscalar_data_range);
5308 ++i)
5309 data_set_written[i] = true;
5310
5311 // write the header. concatenate all the component names with double
5312 // underscores unless a vector name has been specified
5313 out << " <DataArray type=\"Float32\" Name=\"";
5314
5315 if (!std::get<2>(nonscalar_data_range).empty())
5316 out << std::get<2>(nonscalar_data_range);
5317 else
5318 {
5319 for (unsigned int i = std::get<0>(nonscalar_data_range);
5320 i < std::get<1>(nonscalar_data_range);
5321 ++i)
5322 out << data_names[i] << "__";
5323 out << data_names[std::get<1>(nonscalar_data_range)];
5324 }
5325
5326 out << "\" NumberOfComponents=\"3\"></DataArray>\n";
5327 }
5328
5329 for (unsigned int data_set = 0; data_set < data_names.size();
5330 ++data_set)
5331 if (data_set_written[data_set] == false)
5332 {
5333 out << " <DataArray type=\"Float32\" Name=\""
5334 << data_names[data_set] << "\"></DataArray>\n";
5335 }
5336
5337 out << " </PointData>\n";
5338 out << "</Piece>\n";
5339
5340 out << std::flush;
5341
5342 return;
5343 }
5344#endif
5345
5346 // first up: metadata
5347 //
5348 // if desired, output time and cycle of the simulation, following the
5349 // instructions at
5350 // http://www.visitusers.org/index.php?title=Time_and_Cycle_in_VTK_files
5351 {
5352 const unsigned int n_metadata =
5353 ((flags.cycle != std::numeric_limits<unsigned int>::min() ? 1 : 0) +
5354 (flags.time != std::numeric_limits<double>::min() ? 1 : 0));
5355 if (n_metadata > 0)
5356 out << "<FieldData>\n";
5357
5358 if (flags.cycle != std::numeric_limits<unsigned int>::min())
5359 {
5360 out
5361 << "<DataArray type=\"Float32\" Name=\"CYCLE\" NumberOfTuples=\"1\" format=\"ascii\">"
5362 << flags.cycle << "</DataArray>\n";
5363 }
5364 if (flags.time != std::numeric_limits<double>::min())
5365 {
5366 out
5367 << "<DataArray type=\"Float32\" Name=\"TIME\" NumberOfTuples=\"1\" format=\"ascii\">"
5368 << flags.time << "</DataArray>\n";
5369 }
5370
5371 if (n_metadata > 0)
5372 out << "</FieldData>\n";
5373 }
5374
5375
5376 const unsigned int n_data_sets = data_names.size();
5377 // check against # of data sets in first patch. checks against all other
5378 // patches are made in write_gmv_reorder_data_vectors
5379 if (patches[0].points_are_available)
5380 {
5381 AssertDimension(n_data_sets + spacedim, patches[0].data.n_rows());
5382 }
5383 else
5384 {
5385 AssertDimension(n_data_sets, patches[0].data.n_rows());
5386 }
5387
5388 const char *ascii_or_binary =
5389 (deal_ii_with_zlib &&
5391 "binary" :
5392 "ascii";
5393
5394
5395 // first count the number of cells and cells for later use
5396 unsigned int n_nodes;
5397 unsigned int n_cells;
5398 std::tie(n_nodes, n_cells, std::ignore) =
5399 count_nodes_and_cells_and_points(patches, flags.write_higher_order_cells);
5400
5401 // -----------------
5402 // In the following, let us first set up a number of lambda functions that
5403 // will be used in building the different parts of the VTU file. We will
5404 // later call them in turn on different tasks.
5405 // first make up a list of used vertices along with their coordinates
5406 const auto stringize_vertex_information = [&patches,
5407 &flags,
5408 output_precision =
5409 out.precision(),
5410 ascii_or_binary]() {
5411 std::ostringstream o;
5412 o << " <Points>\n";
5413 o << " <DataArray type=\"Float32\" NumberOfComponents=\"3\" format=\""
5414 << ascii_or_binary << "\">\n";
5415 const std::vector<Point<spacedim>> node_positions =
5416 get_node_positions(patches);
5417
5418 // VTK/VTU always wants to see three coordinates, even if we are
5419 // in 1d or 2d. So pad node positions with zeros as appropriate.
5420 std::vector<float> node_coordinates_3d;
5421 node_coordinates_3d.reserve(node_positions.size() * 3);
5422 for (const auto &node_position : node_positions)
5423 {
5424 for (unsigned int d = 0; d < 3; ++d)
5425 if (d < spacedim)
5426 node_coordinates_3d.emplace_back(node_position[d]);
5427 else
5428 node_coordinates_3d.emplace_back(0.0f);
5429 }
5430 o << vtu_stringize_array(node_coordinates_3d,
5431 flags.compression_level,
5432 output_precision)
5433 << '\n';
5434 o << " </DataArray>\n";
5435 o << " </Points>\n\n";
5436
5437 return o.str();
5438 };
5439
5440
5441 //-------------------------------
5442 // Now for the cells. The first part of this is how vertices
5443 // build cells.
5444 const auto stringize_cell_to_vertex_information = [&patches,
5445 &flags,
5446 ascii_or_binary,
5447 output_precision =
5448 out.precision()]() {
5449 std::ostringstream o;
5450
5451 o << " <Cells>\n";
5452 o << " <DataArray type=\"Int32\" Name=\"connectivity\" format=\""
5453 << ascii_or_binary << "\">\n";
5454
5455 std::vector<int32_t> cells;
5456 Assert(dim <= 3, ExcNotImplemented());
5457
5458 unsigned int first_vertex_of_patch = 0;
5459
5460 for (const auto &patch : patches)
5461 {
5462 // First treat a slight oddball case: For triangles and tetrahedra,
5463 // the case with n_subdivisions==2 is treated as if the cell was
5464 // output as a single, quadratic, cell rather than as one would
5465 // expect as 4 sub-cells (for triangles; and the corresponding
5466 // number of sub-cells for tetrahedra). This is courtesy of some
5467 // special-casing in the function extract_vtk_patch_info().
5468 if ((dim >= 2) &&
5469 (patch.reference_cell == ReferenceCells::get_simplex<dim>()) &&
5470 (patch.n_subdivisions == 2))
5471 {
5472 const unsigned int n_points = patch.data.n_cols();
5473 Assert((dim == 2 && n_points == 6) ||
5474 (dim == 3 && n_points == 10),
5476
5477 if (deal_ii_with_zlib &&
5478 (flags.compression_level !=
5480 {
5481 for (unsigned int i = 0; i < n_points; ++i)
5482 cells.push_back(first_vertex_of_patch + i);
5483 }
5484 else
5485 {
5486 for (unsigned int i = 0; i < n_points; ++i)
5487 o << '\t' << first_vertex_of_patch + i;
5488 o << '\n';
5489 }
5490
5491 first_vertex_of_patch += n_points;
5492 }
5493 // Then treat all of the other non-hypercube cases since they can
5494 // currently not be subdivided (into sub-cells, or into higher-order
5495 // cells):
5496 else if (patch.reference_cell != ReferenceCells::get_hypercube<dim>())
5497 {
5499
5500 const unsigned int n_points = patch.data.n_cols();
5501
5502 if (deal_ii_with_zlib &&
5503 (flags.compression_level !=
5505 {
5506 for (unsigned int i = 0; i < n_points; ++i)
5507 cells.push_back(
5508 first_vertex_of_patch +
5510 }
5511 else
5512 {
5513 for (unsigned int i = 0; i < n_points; ++i)
5514 o << '\t'
5515 << (first_vertex_of_patch +
5517 o << '\n';
5518 }
5519
5520 first_vertex_of_patch += n_points;
5521 }
5522 else // a hypercube cell
5523 {
5524 const unsigned int n_subdivisions = patch.n_subdivisions;
5525 const unsigned int n_points_per_direction = n_subdivisions + 1;
5526
5527 std::vector<unsigned> local_vertex_order;
5528
5529 // Output the current state of the local_vertex_order array,
5530 // then clear it:
5531 const auto flush_current_cell = [&flags,
5532 &o,
5533 &cells,
5534 first_vertex_of_patch,
5535 &local_vertex_order]() {
5536 if (deal_ii_with_zlib &&
5537 (flags.compression_level !=
5539 {
5540 for (const auto &c : local_vertex_order)
5541 cells.push_back(first_vertex_of_patch + c);
5542 }
5543 else
5544 {
5545 for (const auto &c : local_vertex_order)
5546 o << '\t' << first_vertex_of_patch + c;
5547 o << '\n';
5548 }
5549
5550 local_vertex_order.clear();
5551 };
5552
5553 if (flags.write_higher_order_cells == false)
5554 {
5555 local_vertex_order.reserve(Utilities::fixed_power<dim>(2));
5556
5557 switch (dim)
5558 {
5559 case 0:
5560 {
5561 local_vertex_order.emplace_back(0);
5562 flush_current_cell();
5563 break;
5564 }
5565
5566 case 1:
5567 {
5568 for (unsigned int i1 = 0; i1 < n_subdivisions; ++i1)
5569 {
5570 const unsigned int starting_offset = i1;
5571 local_vertex_order.emplace_back(starting_offset);
5572 local_vertex_order.emplace_back(starting_offset +
5573 1);
5574 flush_current_cell();
5575 }
5576 break;
5577 }
5578
5579 case 2:
5580 {
5581 for (unsigned int i2 = 0; i2 < n_subdivisions; ++i2)
5582 for (unsigned int i1 = 0; i1 < n_subdivisions; ++i1)
5583 {
5584 const unsigned int starting_offset =
5585 i2 * n_points_per_direction + i1;
5586 local_vertex_order.emplace_back(
5587 starting_offset);
5588 local_vertex_order.emplace_back(
5589 starting_offset + 1);
5590 local_vertex_order.emplace_back(
5591 starting_offset + n_points_per_direction + 1);
5592 local_vertex_order.emplace_back(
5593 starting_offset + n_points_per_direction);
5594 flush_current_cell();
5595 }
5596 break;
5597 }
5598
5599 case 3:
5600 {
5601 for (unsigned int i3 = 0; i3 < n_subdivisions; ++i3)
5602 for (unsigned int i2 = 0; i2 < n_subdivisions; ++i2)
5603 for (unsigned int i1 = 0; i1 < n_subdivisions;
5604 ++i1)
5605 {
5606 const unsigned int starting_offset =
5607 i3 * n_points_per_direction *
5608 n_points_per_direction +
5609 i2 * n_points_per_direction + i1;
5610 local_vertex_order.emplace_back(
5611 starting_offset);
5612 local_vertex_order.emplace_back(
5613 starting_offset + 1);
5614 local_vertex_order.emplace_back(
5615 starting_offset + n_points_per_direction +
5616 1);
5617 local_vertex_order.emplace_back(
5618 starting_offset + n_points_per_direction);
5619 local_vertex_order.emplace_back(
5620 starting_offset + n_points_per_direction *
5621 n_points_per_direction);
5622 local_vertex_order.emplace_back(
5623 starting_offset +
5624 n_points_per_direction *
5625 n_points_per_direction +
5626 1);
5627 local_vertex_order.emplace_back(
5628 starting_offset +
5629 n_points_per_direction *
5630 n_points_per_direction +
5631 n_points_per_direction + 1);
5632 local_vertex_order.emplace_back(
5633 starting_offset +
5634 n_points_per_direction *
5635 n_points_per_direction +
5636 n_points_per_direction);
5637 flush_current_cell();
5638 }
5639 break;
5640 }
5641
5642 default:
5644 }
5645 }
5646 else // use higher-order output
5647 {
5648 local_vertex_order.resize(
5649 Utilities::fixed_power<dim>(n_points_per_direction));
5650
5651 switch (dim)
5652 {
5653 case 0:
5654 {
5655 Assert(false,
5656 ExcMessage(
5657 "Point-like cells should not be possible "
5658 "when writing higher-order cells."));
5659 break;
5660 }
5661 case 1:
5662 {
5663 for (unsigned int i1 = 0; i1 < n_subdivisions + 1;
5664 ++i1)
5665 {
5666 const unsigned int local_index = i1;
5667 const unsigned int connectivity_index =
5668 patch.reference_cell
5669 .template vtk_lexicographic_to_node_index<1>(
5670 {{i1}},
5671 {{n_subdivisions}},
5672 /* use VTU, not VTK: */ false);
5673 local_vertex_order[connectivity_index] =
5674 local_index;
5675 flush_current_cell();
5676 }
5677
5678 break;
5679 }
5680 case 2:
5681 {
5682 for (unsigned int i2 = 0; i2 < n_subdivisions + 1;
5683 ++i2)
5684 for (unsigned int i1 = 0; i1 < n_subdivisions + 1;
5685 ++i1)
5686 {
5687 const unsigned int local_index =
5688 i2 * n_points_per_direction + i1;
5689 const unsigned int connectivity_index =
5690 patch.reference_cell
5691 .template vtk_lexicographic_to_node_index<
5692 2>({{i1, i2}},
5693 {{n_subdivisions, n_subdivisions}},
5694 /* use VTU, not VTK: */ false);
5695 local_vertex_order[connectivity_index] =
5696 local_index;
5697 }
5698 flush_current_cell();
5699
5700 break;
5701 }
5702 case 3:
5703 {
5704 for (unsigned int i3 = 0; i3 < n_subdivisions + 1;
5705 ++i3)
5706 for (unsigned int i2 = 0; i2 < n_subdivisions + 1;
5707 ++i2)
5708 for (unsigned int i1 = 0; i1 < n_subdivisions + 1;
5709 ++i1)
5710 {
5711 const unsigned int local_index =
5712 i3 * n_points_per_direction *
5713 n_points_per_direction +
5714 i2 * n_points_per_direction + i1;
5715 const unsigned int connectivity_index =
5716 patch.reference_cell
5717 .template vtk_lexicographic_to_node_index<
5718 3>({{i1, i2, i3}},
5719 {{n_subdivisions,
5720 n_subdivisions,
5721 n_subdivisions}},
5722 /* use VTU, not VTK: */ false);
5723 local_vertex_order[connectivity_index] =
5724 local_index;
5725 }
5726
5727 flush_current_cell();
5728 break;
5729 }
5730 default:
5732 }
5733 }
5734
5735 // Finally update the number of the first vertex of this
5736 // patch
5737 first_vertex_of_patch +=
5738 Utilities::fixed_power<dim>(patch.n_subdivisions + 1);
5739 }
5740 }
5741
5742 // Flush the 'cells' object we created herein.
5743 if (deal_ii_with_zlib && (flags.compression_level !=
5745 {
5746 o << vtu_stringize_array(cells,
5747 flags.compression_level,
5748 output_precision)
5749 << '\n';
5750 }
5751 o << " </DataArray>\n";
5752
5753 return o.str();
5754 };
5755
5756
5757 //-------------------------------
5758 // The second part of cell information is the offsets in
5759 // the array built by the previous lambda function that indicate
5760 // individual cells.
5761 //
5762 // Note that this separates XML VTU format from the VTK format; the latter
5763 // puts the number of nodes per cell in front of the connectivity list for
5764 // each cell, whereas the VTU format uses one large list of vertex indices
5765 // and a separate array of offsets.
5766 //
5767 // The third piece to cell information is that we need to
5768 // output the types of the cells.
5769 //
5770 // The following function does both of these pieces.
5771 const auto stringize_cell_offset_and_type_information =
5772 [&patches,
5773 &flags,
5774 ascii_or_binary,
5775 n_cells,
5776 output_precision = out.precision()]() {
5777 std::ostringstream o;
5778
5779 o << " <DataArray type=\"Int32\" Name=\"offsets\" format=\""
5780 << ascii_or_binary << "\">\n";
5781
5782 std::vector<int32_t> offsets;
5783 offsets.reserve(n_cells);
5784
5785 // std::uint8_t might be an alias to unsigned char which is then not
5786 // printed as ascii integers
5787 std::vector<unsigned int> cell_types;
5788 cell_types.reserve(n_cells);
5789
5790 unsigned int first_vertex_of_patch = 0;
5791
5792 for (const auto &patch : patches)
5793 {
5794 const auto vtk_cell_id =
5795 extract_vtk_patch_info(patch, flags.write_higher_order_cells);
5796
5797 for (unsigned int i = 0; i < vtk_cell_id[1]; ++i)
5798 {
5799 cell_types.push_back(vtk_cell_id[0]);
5800 first_vertex_of_patch += vtk_cell_id[2];
5801 offsets.push_back(first_vertex_of_patch);
5802 }
5803 }
5804
5805 o << vtu_stringize_array(offsets,
5806 flags.compression_level,
5807 output_precision);
5808 o << '\n';
5809 o << " </DataArray>\n";
5810
5811 o << " <DataArray type=\"UInt8\" Name=\"types\" format=\""
5812 << ascii_or_binary << "\">\n";
5813
5814 if (deal_ii_with_zlib &&
5816 {
5817 std::vector<uint8_t> cell_types_uint8_t(cell_types.size());
5818 for (unsigned int i = 0; i < cell_types.size(); ++i)
5819 cell_types_uint8_t[i] = static_cast<std::uint8_t>(cell_types[i]);
5820
5821 o << vtu_stringize_array(cell_types_uint8_t,
5822 flags.compression_level,
5823 output_precision);
5824 }
5825 else
5826 {
5827 o << vtu_stringize_array(cell_types,
5828 flags.compression_level,
5829 output_precision);
5830 }
5831
5832 o << '\n';
5833 o << " </DataArray>\n";
5834 o << " </Cells>\n";
5835
5836 return o.str();
5837 };
5838
5839
5840 //-------------------------------------
5841 // data output.
5842
5843 const auto stringize_nonscalar_data_range =
5844 [&flags,
5845 &data_names,
5846 ascii_or_binary,
5847 n_data_sets,
5848 n_nodes,
5849 output_precision = out.precision()](const Table<2, float> &data_vectors,
5850 const auto &range) {
5851 std::ostringstream o;
5852
5853 const auto first_component = std::get<0>(range);
5854 const auto last_component = std::get<1>(range);
5855 const auto &name = std::get<2>(range);
5856 const bool is_tensor =
5857 (std::get<3>(range) ==
5859 const unsigned int n_components = (is_tensor ? 9 : 3);
5860 AssertThrow(last_component >= first_component,
5861 ExcLowerRange(last_component, first_component));
5862 AssertThrow(last_component < n_data_sets,
5863 ExcIndexRange(last_component, 0, n_data_sets));
5864 if (is_tensor)
5865 {
5866 AssertThrow((last_component + 1 - first_component <= 9),
5867 ExcMessage(
5868 "Can't declare a tensor with more than 9 components "
5869 "in VTK/VTU format."));
5870 }
5871 else
5872 {
5873 AssertThrow((last_component + 1 - first_component <= 3),
5874 ExcMessage(
5875 "Can't declare a vector with more than 3 components "
5876 "in VTK/VTU format."));
5877 }
5878
5879 // write the header. concatenate all the component names with double
5880 // underscores unless a vector name has been specified
5881 o << " <DataArray type=\"Float32\" Name=\"";
5882
5883 if (!name.empty())
5884 o << name;
5885 else
5886 {
5887 for (unsigned int i = first_component; i < last_component; ++i)
5888 o << data_names[i] << "__";
5889 o << data_names[last_component];
5890 }
5891
5892 o << "\" NumberOfComponents=\"" << n_components << "\" format=\""
5893 << ascii_or_binary << "\"";
5894 // If present, also list the physical units for this quantity. Look
5895 // this up for either the name of the whole vector/tensor, or if that
5896 // isn't listed, via its first component.
5897 if (!name.empty())
5898 {
5899 if (flags.physical_units.find(name) != flags.physical_units.end())
5900 o << " units=\"" << flags.physical_units.at(name) << "\"";
5901 }
5902 else
5903 {
5904 if (flags.physical_units.find(data_names[first_component]) !=
5905 flags.physical_units.end())
5906 o << " units=\""
5907 << flags.physical_units.at(data_names[first_component]) << "\"";
5908 }
5909 o << ">\n";
5910
5911 // now write data. pad all vectors to have three components
5912 std::vector<float> data;
5913 data.reserve(n_nodes * n_components);
5914
5915 for (unsigned int n = 0; n < n_nodes; ++n)
5916 {
5917 if (!is_tensor)
5918 {
5919 switch (last_component - first_component)
5920 {
5921 case 0:
5922 data.push_back(data_vectors(first_component, n));
5923 data.push_back(0);
5924 data.push_back(0);
5925 break;
5926
5927 case 1:
5928 data.push_back(data_vectors(first_component, n));
5929 data.push_back(data_vectors(first_component + 1, n));
5930 data.push_back(0);
5931 break;
5932
5933 case 2:
5934 data.push_back(data_vectors(first_component, n));
5935 data.push_back(data_vectors(first_component + 1, n));
5936 data.push_back(data_vectors(first_component + 2, n));
5937 break;
5938
5939 default:
5940 // Anything else is not yet implemented
5942 }
5943 }
5944 else
5945 {
5946 Tensor<2, 3> vtk_data;
5947 vtk_data = 0.;
5948
5949 const unsigned int size = last_component - first_component + 1;
5950 if (size == 1)
5951 // 1d, 1 element
5952 {
5953 vtk_data[0][0] = data_vectors(first_component, n);
5954 }
5955 else if (size == 4)
5956 // 2d, 4 elements
5957 {
5958 for (unsigned int c = 0; c < size; ++c)
5959 {
5960 const auto ind =
5962 vtk_data[ind[0]][ind[1]] =
5963 data_vectors(first_component + c, n);
5964 }
5965 }
5966 else if (size == 9)
5967 // 3d 9 elements
5968 {
5969 for (unsigned int c = 0; c < size; ++c)
5970 {
5971 const auto ind =
5973 vtk_data[ind[0]][ind[1]] =
5974 data_vectors(first_component + c, n);
5975 }
5976 }
5977 else
5978 {
5980 }
5981
5982 // now put the tensor into data
5983 // note we pad with zeros because VTK format always wants to
5984 // see a 3x3 tensor, regardless of dimension
5985 for (unsigned int i = 0; i < 3; ++i)
5986 for (unsigned int j = 0; j < 3; ++j)
5987 data.push_back(vtk_data[i][j]);
5988 }
5989 } // loop over nodes
5990
5991 o << vtu_stringize_array(data,
5992 flags.compression_level,
5993 output_precision);
5994 o << '\n';
5995 o << " </DataArray>\n";
5996
5997 return o.str();
5998 };
5999
6000 const auto stringize_scalar_data_set =
6001 [&flags,
6002 &data_names,
6003 ascii_or_binary,
6004 output_precision = out.precision()](const Table<2, float> &data_vectors,
6005 const unsigned int data_set) {
6006 std::ostringstream o;
6007
6008 o << " <DataArray type=\"Float32\" Name=\"" << data_names[data_set]
6009 << "\" format=\"" << ascii_or_binary << "\"";
6010 // If present, also list the physical units for this quantity.
6011 if (flags.physical_units.find(data_names[data_set]) !=
6012 flags.physical_units.end())
6013 o << " units=\"" << flags.physical_units.at(data_names[data_set])
6014 << "\"";
6015
6016 o << ">\n";
6017
6018 const std::vector<float> data(data_vectors[data_set].begin(),
6019 data_vectors[data_set].end());
6020 o << vtu_stringize_array(data,
6021 flags.compression_level,
6022 output_precision);
6023 o << '\n';
6024 o << " </DataArray>\n";
6025
6026 return o.str();
6027 };
6028
6029
6030 // For the format we write here, we need to write all node values relating
6031 // to one variable at a time. We could in principle do this by looping
6032 // over all patches and extracting the values corresponding to the one
6033 // variable we're dealing with right now, and then start the process over
6034 // for the next variable with another loop over all patches.
6035 //
6036 // An easier way is to create a global table that for each variable
6037 // lists all values. This copying of data vectors can be done in the
6038 // background while we're already working on vertices and cells,
6039 // so do this on a separate task and when wanting to write out the
6040 // data, we wait for that task to finish.
6042 create_global_data_table_task = Threads::new_task([&patches]() {
6043 return create_global_data_table<dim, spacedim, float>(patches);
6044 });
6045
6046 // -----------------------------
6047 // Now finally get around to actually doing anything. Let's start with
6048 // running the first three tasks generating the vertex and cell information:
6050 mesh_tasks += Threads::new_task(stringize_vertex_information);
6051 mesh_tasks += Threads::new_task(stringize_cell_to_vertex_information);
6052 mesh_tasks += Threads::new_task(stringize_cell_offset_and_type_information);
6053
6054 // For what follows, we have to have the reordered data available. So wait
6055 // for that task to conclude and get the resulting data table:
6056 const Table<2, float> data_vectors =
6057 std::move(*create_global_data_table_task.return_value());
6058
6059 // Then create the strings for the actual values of the solution vectors,
6060 // again on separate tasks:
6062 // When writing, first write out all vector and tensor data
6063 std::vector<bool> data_set_handled(n_data_sets, false);
6064 for (const auto &range : nonscalar_data_ranges)
6065 {
6066 // Mark these components as already handled:
6067 const auto first_component = std::get<0>(range);
6068 const auto last_component = std::get<1>(range);
6069 for (unsigned int i = first_component; i <= last_component; ++i)
6070 data_set_handled[i] = true;
6071
6072 data_tasks += Threads::new_task([&, range]() {
6073 return stringize_nonscalar_data_range(data_vectors, range);
6074 });
6075 }
6076
6077 // Now do the left over scalar data sets
6078 for (unsigned int data_set = 0; data_set < n_data_sets; ++data_set)
6079 if (data_set_handled[data_set] == false)
6080 {
6081 data_tasks += Threads::new_task([&, data_set]() {
6082 return stringize_scalar_data_set(data_vectors, data_set);
6083 });
6084 }
6085
6086 // Alright, all tasks are now running. Wait for their conclusion and output
6087 // all of the data they have produced:
6088 out << "<Piece NumberOfPoints=\"" << n_nodes << "\" NumberOfCells=\""
6089 << n_cells << "\" >\n";
6090 for (const auto &s : mesh_tasks.return_values())
6091 out << s;
6092 out << " <PointData Scalars=\"scalars\">\n";
6093 for (const auto &s : data_tasks.return_values())
6094 out << s;
6095 out << " </PointData>\n";
6096 out << " </Piece>\n";
6097
6098 // make sure everything now gets to disk
6099 out.flush();
6100
6101 // assert the stream is still ok
6102 AssertThrow(out.fail() == false, ExcIO());
6103 }
6104
6105
6106
6107 void
6109 std::ostream &out,
6110 const std::vector<std::string> &piece_names,
6111 const std::vector<std::string> &data_names,
6112 const std::vector<
6113 std::tuple<unsigned int,
6114 unsigned int,
6115 std::string,
6117 &nonscalar_data_ranges,
6118 const VtkFlags &flags)
6119 {
6120 AssertThrow(out.fail() == false, ExcIO());
6121
6122 // If the user provided physical units, make sure that they don't contain
6123 // quote characters as this would make the VTU file invalid XML and
6124 // probably lead to all sorts of difficult error messages. Other than that,
6125 // trust the user that whatever they provide makes sense somehow.
6126 for (const auto &unit : flags.physical_units)
6127 {
6128 (void)unit;
6129 Assert(
6130 unit.second.find('\"') == std::string::npos,
6131 ExcMessage(
6132 "A physical unit you provided, <" + unit.second +
6133 ">, contained a quotation mark character. This is not allowed."));
6134 }
6135
6136 const unsigned int n_data_sets = data_names.size();
6137
6138 out << "<?xml version=\"1.0\"?>\n";
6139
6140 out << "<!--\n";
6141 out << "#This file was generated by the deal.II library"
6142 << " on " << Utilities::System::get_date() << " at "
6143 << Utilities::System::get_time() << "\n-->\n";
6144
6145 out
6146 << "<VTKFile type=\"PUnstructuredGrid\" version=\"0.1\" byte_order=\"LittleEndian\">\n";
6147 out << " <PUnstructuredGrid GhostLevel=\"0\">\n";
6148 out << " <PPointData Scalars=\"scalars\">\n";
6149
6150 // We need to output in the same order as the write_vtu function does:
6151 std::vector<bool> data_set_written(n_data_sets, false);
6152 for (const auto &nonscalar_data_range : nonscalar_data_ranges)
6153 {
6154 const auto first_component = std::get<0>(nonscalar_data_range);
6155 const auto last_component = std::get<1>(nonscalar_data_range);
6156 const bool is_tensor =
6157 (std::get<3>(nonscalar_data_range) ==
6159 const unsigned int n_components = (is_tensor ? 9 : 3);
6160 AssertThrow(last_component >= first_component,
6161 ExcLowerRange(last_component, first_component));
6162 AssertThrow(last_component < n_data_sets,
6163 ExcIndexRange(last_component, 0, n_data_sets));
6164 if (is_tensor)
6165 {
6166 AssertThrow((last_component + 1 - first_component <= 9),
6167 ExcMessage(
6168 "Can't declare a tensor with more than 9 components "
6169 "in VTK"));
6170 }
6171 else
6172 {
6173 Assert((last_component + 1 - first_component <= 3),
6174 ExcMessage(
6175 "Can't declare a vector with more than 3 components "
6176 "in VTK"));
6177 }
6178
6179 // mark these components as already written:
6180 for (unsigned int i = std::get<0>(nonscalar_data_range);