ryujin 2.1.1 revision 71cdc42292164f8095c0bd62c4ea75ebcfac58fa
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 Description, int dim, typename Number>
23 const MPIEnsemble &mpi_ensemble,
24 const OfflineData<dim, Number> &offline_data,
25 const HyperbolicSystem &hyperbolic_system,
26 const ParabolicSystem &parabolic_system,
27 const InitialPrecomputedVector &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 std::copy(std::begin(View::component_names),
39 std::end(View::component_names),
40 std::back_inserter(error_quantities_));
41
42 add_parameter("error quantities",
43 error_quantities_,
44 "List of conserved, primitive, precomputed, or parabolic "
45 "quantities used in the computation of the error norms.");
46
47 error_normalize_ = true;
48 add_parameter("error normalize",
49 error_normalize_,
50 "Flag to control whether the error should be normalized by "
51 "the corresponding norm of the analytic solution.");
52
53 error_norms_ = {"Linf", "L1", "L2"};
54 add_parameter("error norms",
55 error_norms_,
56 "List of norms that are computed and reported (in the given "
57 "order). Valid choices are Linf, L1, and L2.");
58 }
59
60
61 template <typename Description, int dim, typename Number>
62 void
64 {
65#ifdef DEBUG_OUTPUT
66 std::cout << "ErrorEvaluation<dim, Number>::prepare()" << std::endl;
67#endif
68
69 base_name_ = name;
70
71 selected_components_extractor_.prepare(error_quantities_);
72
73 AssertThrow(!error_norms_.empty(),
74 dealii::ExcMessage("No error norms selected."));
75 for (const auto &norm : error_norms_) {
76 AssertThrow(norm == "Linf" || norm == "L1" || norm == "L2",
77 dealii::ExcMessage("Invalid norm: \"" + norm +
78 "\" is not one of Linf, L1, or L2."));
79 }
80
81 /* Reset the log file and write out a header: */
82
83 if (mpi_ensemble_.world_rank() != 0)
84 return;
85
86 std::ofstream output(base_name_ + "-error.log",
87 std::ofstream::out | std::ofstream::trunc);
88
89 output << "# " << description() << " summed over quantities: ";
90 for (std::size_t k = 0; k < error_quantities_.size(); ++k)
91 output << (k == 0 ? "" : ", ") << error_quantities_[k];
92 output << "\n";
93
94 if (error_normalize_)
95 output << "# (each error is normalized by the corresponding norm of the "
96 "analytic solution)\n";
97
98 output << "# time t";
99 for (const auto &norm : error_norms_)
100 output << "\t" << norm;
101 output << "\n" << std::flush;
102 }
103
104
105 template <typename Description, int dim, typename Number>
107 const StateVector &state_vector, const StateVector &analytic) const
108 {
109#ifdef DEBUG_OUTPUT
110 std::cout << "ErrorEvaluation<dim, Number>::compute()" << std::endl;
111#endif
112
113 /* Ensure that the state vectors are resident on the host memory space. */
115 ComputingTimer::Scope scope("time step [X] _ - memory space transfers");
116 for (const auto *vector : {&state_vector, &analytic}) {
117 const auto &[U, precomputed, parabolic] = *vector;
118 U.template copy_to_memory_space<dealii::MemorySpace::Host>();
119 precomputed.template copy_to_memory_space<dealii::MemorySpace::Host>();
120 }
121 }
122
123 const auto &discretization = offline_data_->discretization();
124 const auto &dof_handler = offline_data_->dof_handler();
125
126 Vector<Number> difference_per_cell(
127 discretization.triangulation().n_active_cells());
128
129 /* Compute the selected norm of a scalar vector: */
130 const auto compute_norm = [&](const ScalarHostVector &vector,
131 const std::string &norm) -> Number {
132 if (norm == "Linf")
133 return vector.linfty_norm();
134
135 VectorTools::integrate_difference(discretization.mapping(),
136 dof_handler,
137 vector,
138 Functions::ZeroFunction<dim, Number>(),
139 difference_per_cell,
140 discretization.quadrature_high_order(),
141 norm == "L1" ? VectorTools::L1_norm
142 : VectorTools::L2_norm);
143
144 if (norm == "L1")
145 return Utilities::MPI::sum(difference_per_cell.l1_norm(),
146 mpi_ensemble_.ensemble_communicator());
147
148 return Number(std::sqrt(
149 Utilities::MPI::sum(std::pow(difference_per_cell.l2_norm(), 2),
150 mpi_ensemble_.ensemble_communicator())));
151 };
152
153 selected_components_extractor_.prepare_extraction(analytic);
154 auto analytic_components = selected_components_extractor_.view().extract();
155
156 selected_components_extractor_.prepare_extraction(state_vector);
157 auto error_components = selected_components_extractor_.view().extract();
158
159 std::vector<Number> norms(error_norms_.size(), Number(0.));
160
161 /* Loop over all selected components: */
162 for (std::size_t k = 0; k < error_quantities_.size(); ++k) {
163 auto &analytic_component = analytic_components[k];
164 auto &error_component = error_components[k];
165
166 analytic_component.update_ghost_values();
167
168 /* Populate constrained dofs due to periodicity: */
169 offline_data_->affine_constraints().distribute(error_component);
170 error_component.update_ghost_values();
171 error_component -= analytic_component;
172
173 for (std::size_t n = 0; n < error_norms_.size(); ++n) {
174 const auto error = compute_norm(error_component, error_norms_[n]);
175 norms[n] += error_normalize_ ? error / compute_norm(analytic_component,
176 error_norms_[n])
177 : error;
178 }
179 }
180
181 /*
182 * Sum up over all participating MPI ranks. Note: we only perform this
183 * operation on "peer" ranks zero:
184 */
185
186 if (mpi_ensemble_.ensemble_rank() == 0 && mpi_ensemble_.n_ensembles() > 1)
187 for (auto &norm : norms)
188 norm = Utilities::MPI::sum(
189 norm, mpi_ensemble_.ensemble_leader_communicator());
190
191 return norms;
192 }
193
194
195 template <typename Description, int dim, typename Number>
197 const StateVector &state_vector,
198 const StateVector &analytic,
199 const Number t) const
200 {
201#ifdef DEBUG_OUTPUT
202 std::cout << "ErrorEvaluation<dim, Number>::write_out()" << std::endl;
203#endif
204
205 const auto norms = compute(state_vector, analytic);
206
207 if (mpi_ensemble_.world_rank() != 0)
208 return;
209
210 std::ofstream output(base_name_ + "-error.log",
211 std::ofstream::out | std::ofstream::app);
212 output << std::scientific << std::setprecision(14);
213
214 output << t;
215 for (const auto &norm : norms)
216 output << "\t" << norm;
217 output << "\n" << std::flush;
218 }
219
220
221 template <typename Description, int dim, typename Number>
223 std::ostream &stream,
224 const Number t,
225 const dealii::types::global_dof_index n_global_dofs,
226 const std::vector<Number> &norms) const
227 {
228 if (mpi_ensemble_.world_rank() != 0)
229 return;
230
231 stream << description() << " at final time \n";
232 stream << std::setprecision(16);
233 stream << "#dofs = " << n_global_dofs << std::endl;
234 stream << "t = " << t << std::endl;
235
236 for (std::size_t n = 0; n < error_norms_.size(); ++n) {
237 const auto &name = error_norms_[n];
238 stream << name << std::string(6 - name.size(), ' ') << "= " << norms[n]
239 << std::endl;
240 }
241 }
242
243
244 template <typename Description, int dim, typename Number>
246 {
247 std::string result =
248 error_normalize_ ? "Normalized consolidated " : "Consolidated ";
249
250 /* Join the norms in natural language: "Linf, L1, and L2" */
251 const auto n = error_norms_.size();
252 for (std::size_t i = 0; i < n; ++i) {
253 if (i > 0)
254 result += (n == 2) ? " and " : (i + 1 == n ? ", and " : ", ");
255 result += error_norms_[i];
256 }
257
258 return result + " errors";
259 }
260
261} /* namespace ryujin */
void print_summary(std::ostream &stream, Number t, dealii::types::global_dof_index n_global_dofs, const std::vector< Number > &norms) const
void write_out(const StateVector &state_vector, const StateVector &analytic, Number t) const
void prepare(const std::string &name)
ErrorEvaluation(const MPIEnsemble &mpi_ensemble, const OfflineData< dim, Number > &offline_data, const HyperbolicSystem &hyperbolic_system, const ParabolicSystem &parabolic_system, const InitialPrecomputedVector &initial_precomputed, const std::string &subsection="/ErrorEvaluation")
typename View::InitialPrecomputedVector InitialPrecomputedVector
typename Description::ParabolicSystem ParabolicSystem
typename Description::HyperbolicSystem HyperbolicSystem
std::vector< Number > compute(const StateVector &state_vector, const StateVector &analytic) const
constexpr bool have_separate_memory_spaces
Definition gpu.h:29