libMesh
adjoint_residual_error_estimator.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 
19 // Local Includes
20 #include "libmesh/adjoint_residual_error_estimator.h"
21 #include "libmesh/error_vector.h"
22 #include "libmesh/patch_recovery_error_estimator.h"
23 #include "libmesh/libmesh_logging.h"
24 #include "libmesh/numeric_vector.h"
25 #include "libmesh/system.h"
26 #include "libmesh/system_norm.h"
27 #include "libmesh/qoi_set.h"
28 #include "libmesh/enum_error_estimator_type.h"
29 #include "libmesh/int_range.h"
30 
31 // C++ includes
32 #include <iostream>
33 #include <iomanip>
34 #include <memory>
35 #include <sstream>
36 
37 namespace libMesh
38 {
39 
40 //-----------------------------------------------------------------
41 // AdjointResidualErrorEstimator implementations
44  error_plot_suffix(),
45  _primal_error_estimator(std::make_unique<PatchRecoveryErrorEstimator>()),
46  _dual_error_estimator(std::make_unique<PatchRecoveryErrorEstimator>()),
47  _qoi_set(QoISet())
48 {
49 }
50 
51 
52 
55 {
56  return ADJOINT_RESIDUAL;
57 }
58 
59 
60 
62  ErrorVector & error_per_cell,
63  const NumericVector<Number> * solution_vector,
64  bool estimate_parent_error)
65 {
66  LOG_SCOPE("estimate_error()", "AdjointResidualErrorEstimator");
67 
68  // The current mesh
69  const MeshBase & mesh = _system.get_mesh();
70 
71  // Resize the error_per_cell vector to be
72  // the number of elements, initialize it to 0.
73  error_per_cell.resize (mesh.max_elem_id());
74  std::fill (error_per_cell.begin(), error_per_cell.end(), 0.);
75 
76  // Get the number of variables in the system
77  unsigned int n_vars = _system.n_vars();
78 
79  // We need to make a map of the pointer to the solution vector
80  std::map<const System *, const NumericVector<Number> *>solutionvecs;
81  solutionvecs[&_system] = _system.solution.get();
82 
83  // Solve the dual problem if we have to
84  if (!_system.is_adjoint_already_solved())
85  {
86  // FIXME - we'll need to change a lot of APIs to make this trick
87  // work with a const System...
88  System & system = const_cast<System &>(_system);
89  system.adjoint_solve(_qoi_set);
90  }
91 
92  // Flag to check whether we have not been asked to weight the variable error contributions in any specific manner
93  bool error_norm_is_identity = error_norm.is_identity();
94 
95  // Create an ErrorMap/ErrorVector to store the primal, dual and total_dual variable errors
96  ErrorMap primal_errors_per_cell;
97  ErrorMap dual_errors_per_cell;
98  ErrorMap total_dual_errors_per_cell;
99  // Allocate ErrorVectors to this map if we're going to use it
100  if (!error_norm_is_identity)
101  for (unsigned int v = 0; v < n_vars; v++)
102  {
103  primal_errors_per_cell[std::make_pair(&_system, v)] = std::make_unique<ErrorVector>();
104  dual_errors_per_cell[std::make_pair(&_system, v)] = std::make_unique<ErrorVector>();
105  total_dual_errors_per_cell[std::make_pair(&_system, v)] = std::make_unique<ErrorVector>();
106  }
107  ErrorVector primal_error_per_cell;
108  ErrorVector dual_error_per_cell;
109  ErrorVector total_dual_error_per_cell;
110 
111  // Have we been asked to weight the variable error contributions in any specific manner
112  if (!error_norm_is_identity) // If we do
113  {
114  // Estimate the primal problem error for each variable
115  _primal_error_estimator->estimate_errors
116  (_system.get_equation_systems(), primal_errors_per_cell, &solutionvecs, estimate_parent_error);
117  }
118  else // If not
119  {
120  // Just get the combined error estimate
121  _primal_error_estimator->estimate_error
122  (_system, primal_error_per_cell, solution_vector, estimate_parent_error);
123  }
124 
125  // Sum and weight the dual error estimate based on our QoISet
126  for (unsigned int i = 0, n_qois = _system.n_qois(); i != n_qois; ++i)
127  {
128  if (_qoi_set.has_index(i))
129  {
130  // Get the weight for the current QoI
131  Real error_weight = _qoi_set.weight(i);
132 
133  // We need to make a map of the pointer to the adjoint solution vector
134  std::map<const System *, const NumericVector<Number> *>adjointsolutionvecs;
135  adjointsolutionvecs[&_system] = &_system.get_adjoint_solution(i);
136 
137  // Have we been asked to weight the variable error contributions in any specific manner
138  if (!error_norm_is_identity) // If we have
139  {
140  _dual_error_estimator->estimate_errors
141  (_system.get_equation_systems(), dual_errors_per_cell, &adjointsolutionvecs,
142  estimate_parent_error);
143  }
144  else // If not
145  {
146  // Just get the combined error estimate
147  _dual_error_estimator->estimate_error
148  (_system, dual_error_per_cell, &(_system.get_adjoint_solution(i)), estimate_parent_error);
149  }
150 
151  std::size_t error_size;
152 
153  // Get the size of the first ErrorMap vector; this will give us the number of elements
154  if (!error_norm_is_identity) // If in non default weights case
155  {
156  error_size = dual_errors_per_cell[std::make_pair(&_system, 0)]->size();
157  }
158  else // If in the standard default weights case
159  {
160  error_size = dual_error_per_cell.size();
161  }
162 
163  // Resize the ErrorVector(s)
164  if (!error_norm_is_identity)
165  {
166  // Loop over variables
167  for (unsigned int v = 0; v < n_vars; v++)
168  {
169  libmesh_assert(!total_dual_errors_per_cell[std::make_pair(&_system, v)]->size() ||
170  total_dual_errors_per_cell[std::make_pair(&_system, v)]->size() == error_size) ;
171  total_dual_errors_per_cell[std::make_pair(&_system, v)]->resize(error_size);
172  }
173  }
174  else
175  {
176  libmesh_assert(!total_dual_error_per_cell.size() ||
177  total_dual_error_per_cell.size() == error_size);
178  total_dual_error_per_cell.resize(error_size);
179  }
180 
181  for (std::size_t e = 0; e != error_size; ++e)
182  {
183  // Have we been asked to weight the variable error contributions in any specific manner
184  if (!error_norm_is_identity) // If we have
185  {
186  // Loop over variables
187  for (unsigned int v = 0; v < n_vars; v++)
188  {
189  // Now fill in total_dual_error ErrorMap with the weight
190  (*total_dual_errors_per_cell[std::make_pair(&_system, v)])[e] +=
191  static_cast<ErrorVectorReal>
192  (error_weight *
193  (*dual_errors_per_cell[std::make_pair(&_system, v)])[e]);
194  }
195  }
196  else // If not
197  {
198  total_dual_error_per_cell[e] +=
199  static_cast<ErrorVectorReal>(error_weight * dual_error_per_cell[e]);
200  }
201  }
202  }
203  }
204 
205  // Do some debugging plots if requested
206  if (!error_plot_suffix.empty())
207  {
208  if (!error_norm_is_identity) // If we have
209  {
210  // Loop over variables
211  for (unsigned int v = 0; v < n_vars; v++)
212  {
213  std::ostringstream primal_out;
214  std::ostringstream dual_out;
215  primal_out << "primal_" << error_plot_suffix << ".";
216  dual_out << "dual_" << error_plot_suffix << ".";
217 
218  primal_out << std::setw(1)
219  << std::setprecision(0)
220  << std::setfill('0')
221  << std::right
222  << v;
223 
224  dual_out << std::setw(1)
225  << std::setprecision(0)
226  << std::setfill('0')
227  << std::right
228  << v;
229 
230  primal_errors_per_cell[std::make_pair(&_system, v)]->plot_error(primal_out.str(), _system.get_mesh());
231  total_dual_errors_per_cell[std::make_pair(&_system, v)]->plot_error(dual_out.str(), _system.get_mesh());
232 
233  primal_out.clear();
234  dual_out.clear();
235  }
236  }
237  else // If not
238  {
239  std::ostringstream primal_out;
240  std::ostringstream dual_out;
241  primal_out << "primal_" << error_plot_suffix ;
242  dual_out << "dual_" << error_plot_suffix ;
243 
244  primal_error_per_cell.plot_error(primal_out.str(), _system.get_mesh());
245  total_dual_error_per_cell.plot_error(dual_out.str(), _system.get_mesh());
246 
247  primal_out.clear();
248  dual_out.clear();
249  }
250  }
251 
252  // Weight the primal error by the dual error using the system norm object
253  // FIXME: we ought to thread this
254  for (auto i : index_range(error_per_cell))
255  {
256  // Have we been asked to weight the variable error contributions in any specific manner
257  if (!error_norm_is_identity) // If we do
258  {
259  // Create Error Vectors to pass to calculate_norm
260  std::vector<Real> cell_primal_error;
261  std::vector<Real> cell_dual_error;
262 
263  for (unsigned int v = 0; v < n_vars; v++)
264  {
265  cell_primal_error.push_back((*primal_errors_per_cell[std::make_pair(&_system, v)])[i]);
266  cell_dual_error.push_back((*total_dual_errors_per_cell[std::make_pair(&_system, v)])[i]);
267  }
268 
269  error_per_cell[i] =
270  static_cast<ErrorVectorReal>
271  (error_norm.calculate_norm(cell_primal_error, cell_dual_error));
272  }
273  else // If not
274  {
275  error_per_cell[i] = primal_error_per_cell[i]*total_dual_error_per_cell[i];
276  }
277  }
278 }
279 
280 } // namespace libMesh
SystemNorm error_norm
When estimating the error in a single system, the error_norm is used to control the scaling and norm ...
Data structure for specifying which Quantities of Interest should be calculated in an adjoint or a pa...
Definition: qoi_set.h:45
unsigned int n_qois() const
Number of currently active quantities of interest.
Definition: system.h:2621
The ErrorVector is a specialization of the StatisticsVector for error data computed on a finite eleme...
Definition: error_vector.h:50
ErrorEstimatorType
Defines an enum for the different types of error estimators which are available.
const EquationSystems & get_equation_systems() const
Definition: system.h:721
MeshBase & mesh
QoISet _qoi_set
A QoISet to handle cases with multiple QoIs available.
The libMesh namespace provides an interface to certain functionality in the library.
std::map< std::pair< const System *, unsigned int >, std::unique_ptr< ErrorVector > > ErrorMap
When calculating many error vectors at once, we need a data structure to hold them all...
const MeshBase & get_mesh() const
Definition: system.h:2358
DIE A HORRIBLE DEATH HERE typedef float ErrorVectorReal
This is the MeshBase class.
Definition: mesh_base.h:75
bool has_index(std::size_t) const
Return whether or not this index is in the set to be calculated.
Definition: qoi_set.h:224
std::string error_plot_suffix
To aid in investigating error estimator behavior, set this string to a suffix with which to plot (pre...
unsigned int n_vars
Manages consistently variables, degrees of freedom, and coefficient vectors.
Definition: system.h:96
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
Definition: system.h:1593
This class holds functions that will estimate the error in a finite element solution on a given mesh...
libmesh_assert(ctx)
virtual std::pair< unsigned int, Real > adjoint_solve(const QoISet &qoi_indices=QoISet())
Solves the adjoint system, for the specified qoi indices, or for every qoi if qoi_indices is nullptr...
Definition: system.h:2647
void plot_error(const std::string &filename, const MeshBase &mesh) const
Plots a data file, of a type determined by looking at the file extension in filename, of the error values on the active elements of mesh.
Definition: error_vector.C:210
std::unique_ptr< ErrorEstimator > _dual_error_estimator
An error estimator for the adjoint problem.
bool is_adjoint_already_solved() const
Accessor for the adjoint_already_solved boolean.
Definition: system.h:409
Real weight(std::size_t) const
Get the weight for this index (default 1.0)
Definition: qoi_set.h:243
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
This class implements the Patch Recovery error indicator.
Real calculate_norm(const std::vector< Real > &v)
Definition: system_norm.C:232
std::unique_ptr< ErrorEstimator > _primal_error_estimator
An error estimator for the forward problem.
NumericVector< Number > & get_adjoint_solution(unsigned int i=0)
Definition: system.C:1245
unsigned int n_vars() const
Definition: system.h:2430
virtual void estimate_error(const System &system, ErrorVector &error_per_cell, const NumericVector< Number > *solution_vector=nullptr, bool estimate_parent_error=false) override
Compute the adjoint-weighted error on each element and place it in the error_per_cell vector...
virtual ErrorEstimatorType type() const override
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition: int_range.h:117