ryujin 2.1.1 revision b04fb4aba7bd97ddfa5994a6b4b6e8586eb15db2
Loading...
Searching...
No Matches
error_evaluation.template.h
Go to the documentation of this file.
1//
2// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3// Copyright (C) 2026 by the ryujin authors
4//
5
6#pragma once
7
8#include "computing_timer.h"
9#include "error_evaluation.h"
10
11#include <deal.II/numerics/vector_tools.h>
12#include <deal.II/numerics/vector_tools.templates.h>
13
14#include <fstream>
15#include <iomanip>
16
17namespace ryujin
18{
19 using namespace dealii;
20
21 template <typename HyperbolicDescription, int dim, typename Number>
23 const MPIEnsemble &mpi_ensemble,
24 const OfflineData<dim, Number> &offline_data,
25 const HyperbolicSystem &hyperbolic_system,
26 const Interface::ParabolicSystem &parabolic_system,
27 const MultiComponentVector<Number> &initial_precomputed,
28 const std::string &subsection /*= "ErrorEvaluation"*/)
29 : ParameterAcceptor(subsection)
30 , mpi_ensemble_(mpi_ensemble)
31 , offline_data_(&offline_data)
32 , selected_components_extractor_(offline_data,
33 hyperbolic_system,
34 parabolic_system,
35 initial_precomputed)
36 , base_name_("")
37 {
38 error_quantities_ = hyperbolic_system.state_names(dim);
39
40 add_parameter("error quantities",
41 error_quantities_,
42 "List of conserved, primitive, precomputed, or parabolic "
43 "quantities used in the computation of the error norms.");
44
45 error_normalize_ = true;
46 add_parameter("error normalize",
47 error_normalize_,
48 "Flag to control whether the error should be normalized by "
49 "the corresponding norm of the analytic solution.");
50
51 error_norms_ = {"Linf", "L1", "L2"};
52 add_parameter("error norms",
53 error_norms_,
54 "List of norms that are computed and reported (in the given "
55 "order). Valid choices are Linf, L1, and L2.");
56 }
57
58
59 template <typename HyperbolicDescription, int dim, typename Number>
61 const std::string &name)
62 {
63#ifdef DEBUG_OUTPUT
64 std::cout << "ErrorEvaluation<dim, Number>::prepare()" << std::endl;
65#endif
66
67 base_name_ = name;
68
69 selected_components_extractor_.prepare(error_quantities_);
71 AssertThrow(!error_norms_.empty(),
72 dealii::ExcMessage("No error norms selected."));
73 for (const auto &norm : error_norms_) {
74 AssertThrow(norm == "Linf" || norm == "L1" || norm == "L2",
75 dealii::ExcMessage("Invalid norm: \"" + norm +
76 "\" is not one of Linf, L1, or L2."));
77 }
78
79 /* Reset the log file and write out a header: */
80
81 if (mpi_ensemble_.world_rank() != 0)
82 return;
83
84 std::ofstream output(base_name_ + "-error.log",
85 std::ofstream::out | std::ofstream::trunc);
87 output << "# " << description() << " summed over quantities: ";
88 for (std::size_t k = 0; k < error_quantities_.size(); ++k)
89 output << (k == 0 ? "" : ", ") << error_quantities_[k];
90 output << "\n";
91
92 if (error_normalize_)
93 output << "# (each error is normalized by the corresponding norm of the "
94 "analytic solution)\n";
95
96 output << "# time t";
97 for (const auto &norm : error_norms_)
98 output << "\t" << norm;
99 output << "\n" << std::flush;
100 }
101
102
103 template <typename HyperbolicDescription, int dim, typename Number>
104 std::vector<Number>
106 const StateVector<Number> &state_vector,
107 const StateVector<Number> &analytic) const
108 {
109#ifdef DEBUG_OUTPUT
110 std::cout << "ErrorEvaluation<dim, Number>::compute()" << std::endl;
111#endif
112
113 const auto &discretization = offline_data_->discretization();
114 const auto &dof_handler = offline_data_->dof_handler();
115
116 Vector<Number> difference_per_cell(
117 discretization.triangulation().n_active_cells());
118
119 /* Compute the selected norm of a scalar vector: */
120 const auto compute_norm = [&](const ScalarHostVector<Number> &vector,
121 const std::string &norm) -> Number {
122 if (norm == "Linf")
123 return vector.linfty_norm();
124
125 VectorTools::integrate_difference(discretization.mapping(),
126 dof_handler,
127 vector,
128 Functions::ZeroFunction<dim, Number>(),
129 difference_per_cell,
130 discretization.quadrature_high_order(),
131 norm == "L1" ? VectorTools::L1_norm
132 : VectorTools::L2_norm);
133
134 if (norm == "L1")
135 return Utilities::MPI::sum(difference_per_cell.l1_norm(),
136 mpi_ensemble_.ensemble_communicator());
137
138 return Number(std::sqrt(
139 Utilities::MPI::sum(std::pow(difference_per_cell.l2_norm(), 2),
140 mpi_ensemble_.ensemble_communicator())));
141 };
142
143 selected_components_extractor_.prepare_extraction(analytic);
144 auto analytic_components = selected_components_extractor_.view().extract();
145
146 selected_components_extractor_.prepare_extraction(state_vector);
147 auto error_components = selected_components_extractor_.view().extract();
148
149 std::vector<Number> norms(error_norms_.size(), Number(0.));
150
151 /* Loop over all selected components: */
152 for (std::size_t k = 0; k < error_quantities_.size(); ++k) {
153 auto &analytic_component = analytic_components[k];
154 auto &error_component = error_components[k];
155
156 analytic_component.update_ghost_values();
157
158 /* Populate constrained dofs due to periodicity: */
159 offline_data_->affine_constraints().distribute(error_component);
160 error_component.update_ghost_values();
161 error_component -= analytic_component;
162
163 for (std::size_t n = 0; n < error_norms_.size(); ++n) {
164 const auto error = compute_norm(error_component, error_norms_[n]);
165 norms[n] += error_normalize_ ? error / compute_norm(analytic_component,
166 error_norms_[n])
167 : error;
168 }
169 }
170
171 /*
172 * Sum up over all participating MPI ranks. Note: we only perform this
173 * operation on "peer" ranks zero:
174 */
175
176 if (mpi_ensemble_.ensemble_rank() == 0 && mpi_ensemble_.n_ensembles() > 1)
177 for (auto &norm : norms)
178 norm = Utilities::MPI::sum(
179 norm, mpi_ensemble_.ensemble_leader_communicator());
180
181 return norms;
182 }
183
184
185 template <typename HyperbolicDescription, int dim, typename Number>
187 const StateVector<Number> &state_vector,
188 const StateVector<Number> &analytic,
189 const Number t) const
190 {
191#ifdef DEBUG_OUTPUT
192 std::cout << "ErrorEvaluation<dim, Number>::write_out()" << std::endl;
193#endif
194
195 const auto norms = compute(state_vector, analytic);
196
197 if (mpi_ensemble_.world_rank() != 0)
198 return;
199
200 std::ofstream output(base_name_ + "-error.log",
201 std::ofstream::out | std::ofstream::app);
202 output << std::scientific << std::setprecision(14);
203
204 output << t;
205 for (const auto &norm : norms)
206 output << "\t" << norm;
207 output << "\n" << std::flush;
208 }
209
210
211 template <typename HyperbolicDescription, int dim, typename Number>
213 std::ostream &stream,
214 const Number t,
215 const dealii::types::global_dof_index n_global_dofs,
216 const std::vector<Number> &norms) const
217 {
218 if (mpi_ensemble_.world_rank() != 0)
219 return;
220
221 stream << description() << " at final time \n";
222 stream << std::setprecision(16);
223 stream << "#dofs = " << n_global_dofs << std::endl;
224 stream << "t = " << t << std::endl;
225
226 for (std::size_t n = 0; n < error_norms_.size(); ++n) {
227 const auto &name = error_norms_[n];
228 stream << name << std::string(6 - name.size(), ' ') << "= " << norms[n]
229 << std::endl;
230 }
231 }
232
233
234 template <typename HyperbolicDescription, int dim, typename Number>
235 std::string
237 {
238 std::string result =
239 error_normalize_ ? "Normalized consolidated " : "Consolidated ";
240
241 /* Join the norms in natural language: "Linf, L1, and L2" */
242 const auto n = error_norms_.size();
243 for (std::size_t i = 0; i < n; ++i) {
244 if (i > 0)
245 result += (n == 2) ? " and " : (i + 1 == n ? ", and " : ", ");
246 result += error_norms_[i];
247 }
248
249 return result + " errors";
250 }
251
252} /* namespace ryujin */
typename HyperbolicDescription::HyperbolicSystem HyperbolicSystem
void print_summary(std::ostream &stream, Number t, dealii::types::global_dof_index n_global_dofs, const std::vector< Number > &norms) const
std::vector< Number > compute(const StateVector< Number > &state_vector, const StateVector< Number > &analytic) const
void prepare(const std::string &name)
void write_out(const StateVector< Number > &state_vector, const StateVector< Number > &analytic, Number t) const
ErrorEvaluation(const MPIEnsemble &mpi_ensemble, const OfflineData< dim, Number > &offline_data, const HyperbolicSystem &hyperbolic_system, const Interface::ParabolicSystem &parabolic_system, const MultiComponentVector< Number > &initial_precomputed, const std::string &subsection="/ErrorEvaluation")
dealii::LinearAlgebra::distributed::Vector< Number > ScalarHostVector