ryujin 2.1.1 revision d1a5601757449924e68a428cfd892dfe8915810d
vtu_output.template.h
Go to the documentation of this file.
1//
2// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3// Copyright (C) 2020 - 2024 by the ryujin authors
4//
5
6#pragma once
7
8#include "vtu_output.h"
9
10#include <deal.II/base/function_parser.h>
11#include <deal.II/numerics/data_out.h>
12#include <deal.II/numerics/vector_tools.h>
13
14
15namespace ryujin
16{
17 using namespace dealii;
18
19
20 template <typename Description, int dim, typename Number>
22 const MPI_Comm &mpi_communicator,
23 const OfflineData<dim, Number> &offline_data,
24 const HyperbolicModule<Description, dim, Number> &hyperbolic_module,
25 const Postprocessor<Description, dim, Number> &postprocessor,
26 const std::string &subsection /*= "VTUOutput"*/)
27 : ParameterAcceptor(subsection)
28 , mpi_communicator_(mpi_communicator)
29 , offline_data_(&offline_data)
30 , hyperbolic_module_(&hyperbolic_module)
31 , postprocessor_(&postprocessor)
32 , need_to_prepare_step_(false)
33 {
34 use_mpi_io_ = true;
35 add_parameter("use mpi io",
36 use_mpi_io_,
37 "If enabled write out one vtu file via MPI IO using "
38 "write_vtu_in_parallel() instead of independent output files "
39 "via write_vtu_with_pvtu_record()");
40
41 add_parameter("manifolds",
42 manifolds_,
43 "List of level set functions. The description is used to "
44 "only output cells that intersect the given level set.");
45
46 std::copy(std::begin(View::component_names),
47 std::end(View::component_names),
48 std::back_inserter(vtu_output_quantities_));
49
50 std::copy(std::begin(View::initial_precomputed_names),
51 std::end(View::initial_precomputed_names),
52 std::back_inserter(vtu_output_quantities_));
53
54 add_parameter("vtu output quantities",
55 vtu_output_quantities_,
56 "List of conserved, primitive, precomputed, or postprocessed "
57 "quantities that will be written to the vtu files.");
58 }
59
60
61 template <typename Description, int dim, typename Number>
63 {
64#ifdef DEBUG_OUTPUT
65 std::cout << "VTUOutput<dim, Number>::prepare()" << std::endl;
66#endif
67
68 need_to_prepare_step_ = false;
69
70 /* Populate quantities mapping: */
71
72 quantities_mapping_.clear();
73
74 for (const auto &entry : vtu_output_quantities_) {
75 {
76 /* Conserved quantities: */
77
78 constexpr auto &names = View::component_names;
79 const auto pos = std::find(std::begin(names), std::end(names), entry);
80 if (pos != std::end(names)) {
81 const auto index = std::distance(std::begin(names), pos);
82 quantities_mapping_.push_back(std::make_tuple(
83 entry,
84 [index](ScalarVector &result, const StateVector &state_vector) {
85 const auto &U = std::get<0>(state_vector);
86 U.extract_component(result, index);
87 }));
88 continue;
89 }
90 }
91
92 {
93 /* Primitive quantities: */
94
95 constexpr auto &names = View::primitive_component_names;
96 const auto pos = std::find(std::begin(names), std::end(names), entry);
97 if (pos != std::end(names)) {
98 const auto index = std::distance(std::begin(names), pos);
99 quantities_mapping_.push_back(std::make_tuple(
100 entry,
101 [this, index](ScalarVector &result,
102 const StateVector &state_vector) {
103 const auto &U = std::get<0>(state_vector);
104 /*
105 * FIXME: We might traverse the same vector multiple times. This
106 * is inefficient.
107 */
108 const unsigned int n_owned = offline_data_->n_locally_owned();
109 for (unsigned int i = 0; i < n_owned; ++i) {
110 const auto view = hyperbolic_module_->hyperbolic_system()
111 .template view<dim, Number>();
112 result.local_element(i) =
113 view.to_primitive_state(U.get_tensor(i))[index];
114 }
115 }));
116 continue;
117 }
118 }
119
120 {
121 /* Precomputed initial quantities: */
122
123 constexpr auto &names = View::initial_precomputed_names;
124 const auto pos = std::find(std::begin(names), std::end(names), entry);
125 if (pos != std::end(names)) {
126 const auto index = std::distance(std::begin(names), pos);
127 quantities_mapping_.push_back(std::make_tuple(
128 entry, [this, index](ScalarVector &result, const StateVector &) {
129 hyperbolic_module_->initial_precomputed().extract_component(
130 result, index);
131 }));
132 continue;
133 }
134 }
135
136 {
137 /* Precomputed quantities: */
138
139 constexpr auto &names = View::precomputed_names;
140 const auto pos = std::find(std::begin(names), std::end(names), entry);
141 if (pos != std::end(names)) {
142 const auto index = std::distance(std::begin(names), pos);
143 quantities_mapping_.push_back(std::make_tuple(
144 entry,
145 [index](ScalarVector &result, const StateVector &state_vector) {
146 const auto &precomputed = std::get<1>(state_vector);
147 precomputed.extract_component(result, index);
148 }));
149 need_to_prepare_step_ = true;
150 continue;
151 }
152 }
153
154 {
155 /* Special indicator value: */
156
157 if (entry == "alpha") {
158 quantities_mapping_.push_back(std::make_tuple(
159 entry, [this](ScalarVector &result, const StateVector &) {
160 const auto &alpha = hyperbolic_module_->alpha();
161 result = alpha;
162 }));
163 need_to_prepare_step_ = true;
164 continue;
165 }
166 }
167
168 AssertThrow(false, ExcMessage("Invalid component name »" + entry + "«"));
169 }
170
171 quantities_.resize(quantities_mapping_.size());
172 for (auto &it : quantities_)
173 it.reinit(offline_data_->scalar_partitioner());
174 }
175
176
177 template <typename Description, int dim, typename Number>
179 const StateVector &state_vector,
180 std::string name,
181 Number t,
182 unsigned int cycle,
183 bool output_full,
184 bool output_levelsets)
185 {
186#ifdef DEBUG_OUTPUT
187 std::cout << "VTUOutput<dim, Number>::schedule_output()" << std::endl;
188#endif
189 const auto &affine_constraints = offline_data_->affine_constraints();
190
191 /* Copy quantities: */
192
193 Assert(quantities_.size() == quantities_mapping_.size(),
194 ExcInternalError());
195 for (unsigned int d = 0; d < quantities_.size(); ++d) {
196 const auto &lambda = std::get<1>(quantities_mapping_[d]);
197 lambda(quantities_[d], state_vector);
198 affine_constraints.distribute(quantities_[d]);
199 quantities_[d].update_ghost_values();
200 }
201
202 /* prepare DataOut: */
203
204 auto data_out = std::make_unique<dealii::DataOut<dim>>();
205 data_out->attach_dof_handler(offline_data_->dof_handler());
206
207 for (unsigned int d = 0; d < quantities_.size(); ++d) {
208 const auto &entry = std::get<0>(quantities_mapping_[d]);
209 data_out->add_data_vector(quantities_[d], entry);
210 }
211
212 const auto n_quantities = postprocessor_->n_quantities();
213 for (unsigned int i = 0; i < n_quantities; ++i)
214 data_out->add_data_vector(postprocessor_->quantities()[i],
215 postprocessor_->component_names()[i]);
216
217 DataOutBase::VtkFlags flags(t,
218 cycle,
219 true,
220#if DEAL_II_VERSION_GTE(9, 5, 0)
221 DataOutBase::CompressionLevel::best_speed);
222#else
223 DataOutBase::VtkFlags::best_speed);
224#endif
225 data_out->set_flags(flags);
226
227 const auto &discretization = offline_data_->discretization();
228 const auto &mapping = discretization.mapping();
229 const auto patch_order =
230 std::max(1u, discretization.finite_element().degree) - 1u;
231
232 /* Perform output: */
233
234 if (output_full) {
235 data_out->build_patches(mapping, patch_order);
236
237 if (use_mpi_io_) {
238 /* MPI-based synchronous IO */
239 data_out->write_vtu_in_parallel(
240 name + "_" + Utilities::to_string(cycle, 6) + ".vtu",
241 mpi_communicator_);
242 } else {
243 data_out->write_vtu_with_pvtu_record(
244 "", name, cycle, mpi_communicator_, 6);
245 }
246 }
247
248 if (output_levelsets && manifolds_.size() != 0) {
249 /*
250 * Specify an output filter that selects only cells for output that are
251 * in the viscinity of a specified set of output planes:
252 */
253
254 std::vector<std::shared_ptr<FunctionParser<dim>>> level_set_functions;
255 for (const auto &expression : manifolds_)
256 level_set_functions.emplace_back(
257 std::make_shared<FunctionParser<dim>>(expression));
258
259 data_out->set_cell_selection([level_set_functions](const auto &cell) {
260 if (!cell->is_active() || cell->is_artificial())
261 return false;
262
263 for (const auto &function : level_set_functions) {
264
265 unsigned int above = 0;
266 unsigned int below = 0;
267
268 for (unsigned int v = 0; v < GeometryInfo<dim>::vertices_per_cell;
269 ++v) {
270 const auto vertex = cell->vertex(v);
271 constexpr auto eps = std::numeric_limits<Number>::epsilon();
272 if (function->value(vertex) >= 0. - 100. * eps)
273 above++;
274 if (function->value(vertex) <= 0. + 100. * eps)
275 below++;
276 if (above > 0 && below > 0)
277 return true;
278 }
279 }
280 return false;
281 });
282
283 data_out->build_patches(mapping, patch_order);
284
285 if (use_mpi_io_) {
286 /* MPI-based synchronous IO */
287 data_out->write_vtu_in_parallel(
288 name + "-levelsets_" + Utilities::to_string(cycle, 6) + ".vtu",
289 mpi_communicator_);
290 } else {
291 data_out->write_vtu_with_pvtu_record(
292 "", name + "-levelsets", cycle, mpi_communicator_, 6);
293 }
294 }
295
296 /* Explicitly delete pointer to free up memory early: */
297 data_out.reset();
298 }
299
300} /* namespace ryujin */
VTUOutput(const MPI_Comm &mpi_communicator, const OfflineData< dim, Number > &offline_data, const HyperbolicModule< Description, dim, Number > &hyperbolic_module, const Postprocessor< Description, dim, Number > &postprocessor, const std::string &subsection="/VTUOutput")
void schedule_output(const StateVector &state_vector, std::string name, Number t, unsigned int cycle, bool output_full=true, bool output_cutplanes=true)
dealii::LinearAlgebra::distributed::Vector< Number > ScalarVector
Definition: state_vector.h:25
std::tuple< MultiComponentVector< Number, problem_dim >, MultiComponentVector< Number, prec_dim >, BlockVector< Number > > StateVector
Definition: state_vector.h:45