GridFormat 0.6.0
I/O-Library for grid-like data structures
Loading...
Searching...
No Matches
pvtr_writer.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2022-2023 Dennis Gläser <dennis.glaeser@iws.uni-stuttgart.de>
2// SPDX-License-Identifier: MIT
8#ifndef GRIDFORMAT_VTK_PVTR_WRITER_HPP_
9#define GRIDFORMAT_VTK_PVTR_WRITER_HPP_
10
11#include <ostream>
12#include <string>
13#include <algorithm>
14#include <filesystem>
15#include <array>
16#include <tuple>
17
19#include <gridformat/common/ranges.hpp>
20#include <gridformat/common/exceptions.hpp>
21#include <gridformat/common/lvalue_reference.hpp>
22#include <gridformat/common/output_file.hpp>
23
24#include <gridformat/parallel/communication.hpp>
25#include <gridformat/parallel/helpers.hpp>
26
27#include <gridformat/grid/grid.hpp>
28#include <gridformat/xml/element.hpp>
31
32namespace GridFormat {
33
38template<Concepts::RectilinearGrid Grid,
39 Concepts::Communicator Communicator>
40class PVTRWriter : public VTK::XMLWriterBase<Grid, PVTRWriter<Grid, Communicator>> {
42 using CT = CoordinateType<Grid>;
43
44 static constexpr std::size_t space_dim = 3;
45 static constexpr std::size_t dim = dimension<Grid>;
46 static constexpr int root_rank = 0;
47
48 public:
49 explicit PVTRWriter(LValueReferenceOf<const Grid> grid,
50 Communicator comm,
51 VTK::XMLOptions xml_opts = {})
52 : ParentType(grid.get(), ".pvtr", true, xml_opts)
53 , _comm(comm)
54 {}
55
56 const Communicator& communicator() const {
57 return _comm;
58 }
59
60 private:
61 Communicator _comm;
62
63 PVTRWriter _with(VTK::XMLOptions xml_opts) const override {
64 return PVTRWriter{this->grid(), _comm, std::move(xml_opts)};
65 }
66
67 void _write(std::ostream&) const override {
68 throw InvalidState(
69 "PVTRWriter does not support direct export into stream. "
70 "Use overload with filename instead!"
71 );
72 }
73
74 virtual void _write(const std::string& filename_with_ext) const override {
75 const auto& local_extents = extents(this->grid());
76 const auto [origin, is_negative_axis] = _get_origin_and_orientations();
77
79 const auto all_origins = Parallel::gather(_comm, origin, root_rank);
80 const auto all_extents = Parallel::gather(_comm, local_extents, root_rank);
81 const auto [exts_begin, exts_end, whole_extent, _] = helper.compute_extents_and_origin(
82 all_origins,
83 all_extents,
84 is_negative_axis
85 );
86
87 const auto my_whole_extent = Parallel::broadcast(_comm, whole_extent, root_rank);
88 const auto my_extent_offset = Parallel::scatter(_comm, Ranges::flat(exts_begin), root_rank);
89
90 // the collective invocations also ensure that all pieces are written before the main file,
91 // and that the main file is written before returning
92 Parallel::invoke_collectively(_comm, [&] () {
93 _write_piece(filename_with_ext, Ranges::to_array<dim>(my_extent_offset), {my_whole_extent});
94 }, "Writing the pieces of '" + filename_with_ext + "'");
95 Parallel::invoke_collectively(_comm, [&] () {
96 if (Parallel::rank(_comm) == 0)
97 _write_pvtr_file(filename_with_ext, my_whole_extent, exts_begin, exts_end);
98 }, "Writing '" + filename_with_ext + "'");
99 }
100
101 auto _get_origin_and_orientations() const {
102 std::array<CT, dim> origin;
103 std::array<bool, dim> is_negative_axis;
104 for (unsigned dir = 0; dir < dim; ++dir) {
105 std::array<CT, 2> ordinates_01{0, 0};
106 const auto& dir_ordinates = ordinates(this->grid(), dir);
107 std::ranges::copy(std::views::take(dir_ordinates, 2), ordinates_01.begin());
108 origin[dir] = ordinates_01[0];
109 is_negative_axis[dir] = ordinates_01[1] - ordinates_01[0] < CT{0};
110 }
111 return std::make_tuple(origin, is_negative_axis);
112 }
113
114 void _write_piece(const std::string& par_filename,
115 const std::array<std::size_t, dim>& offset,
116 typename VTRWriter<Grid>::Domain domain) const {
117 auto writer = VTRWriter{this->grid(), this->_xml_opts}
118 .as_piece_for(std::move(domain))
119 .with_offset(offset);
120 this->copy_fields(writer);
121 writer.write(PVTK::piece_basefilename(par_filename, Parallel::rank(_comm)));
122 }
123
124 void _write_pvtr_file(const std::string& filename_with_ext,
125 const std::array<std::size_t, dim>& extents,
126 const std::vector<std::array<std::size_t, dim>>& proc_extents_begin,
127 const std::vector<std::array<std::size_t, dim>>& proc_extents_end) const {
128 XMLElement pvtk_xml("VTKFile");
129 pvtk_xml.set_attribute("type", "PRectilinearGrid");
130
131 XMLElement& grid = pvtk_xml.add_child("PRectilinearGrid");
132 grid.set_attribute("WholeExtent", VTK::CommonDetail::extents_string(extents));
133
134 XMLElement& ppoint_data = grid.add_child("PPointData");
135 XMLElement& pcell_data = grid.add_child("PCellData");
136 XMLElement& pcoords = grid.add_child("PCoordinates");
137 std::visit([&] (const auto& encoder) {
138 std::visit([&] (const auto& data_format) {
139 PVTK::PDataArrayHelper pdata_helper{encoder, data_format, ppoint_data};
140 PVTK::PDataArrayHelper cdata_helper{encoder, data_format, pcell_data};
141 std::ranges::for_each(this->_point_field_names(), [&] (const std::string& name) {
142 pdata_helper.add(name, this->_get_point_field(name));
143 });
144 std::ranges::for_each(this->_cell_field_names(), [&] (const std::string& name) {
145 cdata_helper.add(name, this->_get_cell_field(name));
146 });
147
148 std::visit([&] <typename T> (const Precision<T>& prec) {
149 for (unsigned int i = 0; i < space_dim; ++i) {
150 XMLElement& pdata_array = pcoords.add_child("PDataArray");
151 pdata_array.set_attribute("NumberOfComponents", "1");
152 pdata_array.set_attribute("Name", "X_" + std::to_string(i));
153 pdata_array.set_attribute("format", VTK::data_format_name(encoder, data_format));
154 pdata_array.set_attribute("type", VTK::attribute_name(prec));
155 }
156 }, this->_xml_settings.coordinate_precision);
157 }, this->_xml_settings.data_format);
158 }, this->_xml_settings.encoder);
159
160 std::ranges::for_each(Parallel::ranks(_comm), [&] (int rank) {
161 std::array<std::size_t, dim> extents_begin;
162 std::array<std::size_t, dim> extents_end;
163 for (unsigned dir = 0; dir < dim; ++dir) {
164 extents_begin[dir] = proc_extents_begin[rank][dir];
165 extents_end[dir] = proc_extents_end[rank][dir];
166 }
167 auto& piece = grid.add_child("Piece");
168 piece.set_attribute("Extent", VTK::CommonDetail::extents_string(extents_begin, extents_end));
169 piece.set_attribute("Source", std::filesystem::path{
170 PVTK::piece_basefilename(filename_with_ext, rank) + ".vtr"
171 }.filename());
172 });
173
174 this->_set_default_active_fields(pvtk_xml.get_child("PRectilinearGrid"));
175 write_to(filename_with_ext, [&] (std::ostream& s) {
176 write_xml_with_version_header(pvtk_xml, s, Indentation{{.width = 2}});
177 });
178 }
179};
180
181template<typename G, Concepts::Communicator C>
182PVTRWriter(G&&, const C&, VTK::XMLOptions = {}) -> PVTRWriter<std::remove_cvref_t<G>, C>;
183
184namespace Traits {
185
186template<typename... Args>
187struct WritesConnectivity<PVTRWriter<Args...>> : public std::false_type {};
188
189} // namespace Traits
190} // namespace GridFormat
191
192#endif // GRIDFORMAT_VTK_PVTR_WRITER_HPP_
Helper to add a PDataArray child to an xml element.
Definition parallel.hpp:45
Writer for parallel .pvtr files.
Definition pvtr_writer.hpp:40
Base class for VTK-XML Writer implementations.
Definition xml.hpp:192
Writer for .vtr file format.
Definition vtr_writer.hpp:35
Concept for grids to be used as rectilinear grids.
Definition concepts.hpp:66
Abstract interface for fields of values that is used by writers/readers to store fields.
Helper function for writing parallel VTK files.
std::string piece_basefilename(const std::string &par_filename, int rank)
Return the piece filename (w/o extension) for the given rank.
Definition parallel.hpp:38
Can be specialized by writers in case the file format does not contain connectivity information.
Definition writer.hpp:37
Options for VTK-XML files for setting the desired encoding, data format and compression.
Definition xml.hpp:101
Definition vtr_writer.hpp:43
Writer for .vtr file format.