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