libMesh
Public Types | Public Member Functions | Public Attributes | Protected Member Functions | Protected Attributes | List of all members
libMesh::AdjointRefinementEstimator Class Reference

This class implements a "brute force" goal-oriented error estimator which computes an estimate of error in a quantity of interest based on the residual of the current coarse grid primal solution as weighted against an adjoint solution on a uniformly refined (in h and/or p, for an arbitrary number of levels) grid. More...

#include <adjoint_refinement_estimator.h>

Inheritance diagram for libMesh::AdjointRefinementEstimator:
[legend]

Public Types

typedef std::map< std::pair< const System *, unsigned int >, ErrorVector * > ErrorMap
 When calculating many error vectors at once, we need a data structure to hold them all. More...
 

Public Member Functions

 AdjointRefinementEstimator ()
 Constructor. More...
 
 AdjointRefinementEstimator (const AdjointRefinementEstimator &)=default
 Copy/move ctor, copy/move assignment operator, and destructor are all explicitly defaulted for this class. More...
 
 AdjointRefinementEstimator (AdjointRefinementEstimator &&)=default
 
AdjointRefinementEstimatoroperator= (const AdjointRefinementEstimator &)=default
 
AdjointRefinementEstimatoroperator= (AdjointRefinementEstimator &&)=default
 
virtual ~AdjointRefinementEstimator ()=default
 
QoISetqoi_set ()
 Access to the QoISet (default: weight all QoIs equally) to use when computing errors. More...
 
const QoISetqoi_set () const
 Access to the QoISet (default: weight all QoIs equally) to use when computing errors. More...
 
virtual void estimate_error (const System &system, ErrorVector &error_per_cell, const NumericVector< Number > *solution_vector=nullptr, bool estimate_parent_error=false)
 This function does uniform refinements and an adjoint solve to get an adjoint solution on each cell, then estimates the error by finding the weighted residual of the coarse solution with the fine adjoint solution. More...
 
Numberget_global_QoI_error_estimate (unsigned int qoi_index)
 This is an accessor function to access the computed global QoI error estimates. More...
 
virtual ErrorEstimatorType type () const
 
DifferentiablePhysicsget_residual_evaluation_physics ()
 
void set_residual_evaluation_physics (DifferentiablePhysics *set_physics)
 Set the _residual_evaluation_physics member to argument. More...
 
virtual void estimate_errors (const EquationSystems &equation_systems, ErrorVector &error_per_cell, const std::map< const System *, SystemNorm > &error_norms, const std::map< const System *, const NumericVector< Number > * > *solution_vectors=nullptr, bool estimate_parent_error=false)
 This virtual function can be redefined in derived classes, but by default computes the sum of the error_per_cell for each system in the equation_systems. More...
 
virtual void estimate_errors (const EquationSystems &equation_systems, ErrorMap &errors_per_cell, const std::map< const System *, const NumericVector< Number > * > *solution_vectors=nullptr, bool estimate_parent_error=false)
 This virtual function can be redefined in derived classes, but by default it calls estimate_error repeatedly to calculate the requested error vectors. More...
 

Public Attributes

unsigned char number_h_refinements
 How many h refinements to perform to get the fine grid. More...
 
unsigned char number_p_refinements
 How many p refinements to perform to get the fine grid. More...
 
SystemNorm error_norm
 When estimating the error in a single system, the error_norm is used to control the scaling and norm choice for each variable. More...
 

Protected Member Functions

void reduce_error (std::vector< ErrorVectorReal > &error_per_cell, const Parallel::Communicator &comm) const
 This method takes the local error contributions in error_per_cell from each processor and combines them to get the global error vector. More...
 

Protected Attributes

DifferentiablePhysics_residual_evaluation_physics
 Pointer to object to use for physics assembly evaluations. More...
 
std::vector< Numbercomputed_global_QoI_errors
 
QoISet _qoi_set
 A QoISet to handle cases with multiple QoIs available. More...
 

Detailed Description

This class implements a "brute force" goal-oriented error estimator which computes an estimate of error in a quantity of interest based on the residual of the current coarse grid primal solution as weighted against an adjoint solution on a uniformly refined (in h and/or p, for an arbitrary number of levels) grid.

Author
Roy H. Stogner
Date
2009

Definition at line 50 of file adjoint_refinement_estimator.h.

Member Typedef Documentation

◆ ErrorMap

typedef std::map<std::pair<const System *, unsigned int>, ErrorVector *> libMesh::ErrorEstimator::ErrorMap
inherited

When calculating many error vectors at once, we need a data structure to hold them all.

Definition at line 127 of file error_estimator.h.

Constructor & Destructor Documentation

◆ AdjointRefinementEstimator() [1/3]

libMesh::AdjointRefinementEstimator::AdjointRefinementEstimator ( )

Constructor.

Sets the most common default parameter values.

Definition at line 73 of file adjoint_refinement_estimator.C.

73  :
78  _qoi_set(QoISet())
79 {
80  // We're not actually going to use error_norm; our norms are
81  // absolute values of QoI error.
83 }

References libMesh::ErrorEstimator::error_norm, and libMesh::INVALID_NORM.

◆ AdjointRefinementEstimator() [2/3]

libMesh::AdjointRefinementEstimator::AdjointRefinementEstimator ( const AdjointRefinementEstimator )
default

Copy/move ctor, copy/move assignment operator, and destructor are all explicitly defaulted for this class.

◆ AdjointRefinementEstimator() [3/3]

libMesh::AdjointRefinementEstimator::AdjointRefinementEstimator ( AdjointRefinementEstimator &&  )
default

◆ ~AdjointRefinementEstimator()

virtual libMesh::AdjointRefinementEstimator::~AdjointRefinementEstimator ( )
virtualdefault

Member Function Documentation

◆ estimate_error()

void libMesh::AdjointRefinementEstimator::estimate_error ( const System system,
ErrorVector error_per_cell,
const NumericVector< Number > *  solution_vector = nullptr,
bool  estimate_parent_error = false 
)
virtual

This function does uniform refinements and an adjoint solve to get an adjoint solution on each cell, then estimates the error by finding the weighted residual of the coarse solution with the fine adjoint solution.

system.solve() and system.assembly() must be called, and so should have no side effects.

Only the provided system is solved on the refined mesh; we don't support adjoint solves on loosely coupled collections of Systems.

The estimated error is output in the vector error_per_cell

Implements libMesh::ErrorEstimator.

Definition at line 90 of file adjoint_refinement_estimator.C.

94 {
95  // We have to break the rules here, because we can't refine a const System
96  System & system = const_cast<System &>(_system);
97 
98  // An EquationSystems reference will be convenient.
99  EquationSystems & es = system.get_equation_systems();
100 
101  // The current mesh
102  MeshBase & mesh = es.get_mesh();
103 
104  // Get coarse grid adjoint solutions. This should be a relatively
105  // quick (especially with preconditioner reuse) way to get a good
106  // initial guess for the fine grid adjoint solutions. More
107  // importantly, subtracting off a coarse adjoint approximation gives
108  // us better local error indication, and subtracting off *some* lift
109  // function is necessary for correctness if we have heterogeneous
110  // adjoint Dirichlet conditions.
111 
112  // Solve the adjoint problem(s) on the coarse FE space
113  // Only if the user didn't already solve it for us
114  if (!system.is_adjoint_already_solved())
115  system.adjoint_solve(_qoi_set);
116 
117  // Loop over all the adjoint problems and, if any have heterogenous
118  // Dirichlet conditions, get the corresponding coarse lift
119  // function(s)
120  for (unsigned int j=0,
121  n_qois = system.n_qois(); j != n_qois; j++)
122  {
123  // Skip this QoI if it is not in the QoI Set or if there are no
124  // heterogeneous Dirichlet boundaries for it
125  if (_qoi_set.has_index(j) &&
126  system.get_dof_map().has_adjoint_dirichlet_boundaries(j))
127  {
128  // Next, we are going to build up the residual for evaluating the flux QoI
129  NumericVector<Number> * coarse_residual = nullptr;
130 
131  // The definition of a flux QoI is R(u^h, L) where R is the residual as defined
132  // by a conservation law. Therefore, if we are using stabilization, the
133  // R should be specified by the user via the residual_evaluation_physics
134 
135  // If the residual physics pointer is not null, use it when
136  // evaluating here.
137  {
138  const bool swapping_physics = _residual_evaluation_physics;
139  if (swapping_physics)
140  dynamic_cast<DifferentiableSystem &>(system).swap_physics(_residual_evaluation_physics);
141 
142  // Assemble without applying constraints, to capture the solution values on the boundary
143  (dynamic_cast<ImplicitSystem &>(system)).assembly(true, false, false, true);
144 
145  // Get the residual vector (no constraints applied on boundary, so we wont blow away the lift)
146  coarse_residual = &(dynamic_cast<ExplicitSystem &>(system)).get_vector("RHS Vector");
147  coarse_residual->close();
148 
149  // Now build the lift function and add it to the system vectors
150  std::ostringstream liftfunc_name;
151  liftfunc_name << "adjoint_lift_function" << j;
152 
153  system.add_vector(liftfunc_name.str());
154 
155  // Initialize lift with coarse adjoint solve associate with this flux QoI to begin with
156  system.get_vector(liftfunc_name.str()).init(system.get_adjoint_solution(j), false);
157 
158  // Build the actual lift using adjoint dirichlet conditions
159  system.get_dof_map().enforce_adjoint_constraints_exactly
160  (system.get_vector(liftfunc_name.str()), static_cast<unsigned int>(j));
161 
162  // Compute the flux R(u^h, L)
163  std::cout<<"The flux QoI "<<static_cast<unsigned int>(j)<<" is: "<<coarse_residual->dot(system.get_vector(liftfunc_name.str()))<<std::endl<<std::endl;
164 
165  // Swap back if needed
166  if (swapping_physics)
167  dynamic_cast<DifferentiableSystem &>(system).swap_physics(_residual_evaluation_physics);
168  }
169  } // End if QoI in set and flux/dirichlet boundary QoI
170  } // End loop over QoIs
171 
172  // We'll want to back up all coarse grid vectors
173  std::map<std::string, std::unique_ptr<NumericVector<Number>>> coarse_vectors;
174  for (const auto & pr : as_range(system.vectors_begin(), system.vectors_end()))
175  {
176  // The (string) name of this vector
177  const std::string & var_name = pr.first;
178 
179  coarse_vectors[var_name] = pr.second->clone();
180  }
181 
182  // Back up the coarse solution and coarse local solution
183  std::unique_ptr<NumericVector<Number>> coarse_solution = system.solution->clone();
184  std::unique_ptr<NumericVector<Number>> coarse_local_solution = system.current_local_solution->clone();
185 
186  // And we'll need to temporarily change solution projection settings
187  bool old_projection_setting;
188  old_projection_setting = system.project_solution_on_reinit();
189 
190  // Make sure the solution is projected when we refine the mesh
191  system.project_solution_on_reinit() = true;
192 
193  // And it'll be best to avoid any repartitioning
194  std::unique_ptr<Partitioner> old_partitioner(mesh.partitioner().release());
195 
196  // And we can't allow any renumbering
197  const bool old_renumbering_setting = mesh.allow_renumbering();
198  mesh.allow_renumbering(false);
199 
200  // Use a non-standard solution vector if necessary
201  if (solution_vector && solution_vector != system.solution.get())
202  {
203  NumericVector<Number> * newsol =
204  const_cast<NumericVector<Number> *> (solution_vector);
205  newsol->swap(*system.solution);
206  system.update();
207  }
208 
209  // Resize the error_per_cell vector to be
210  // the number of elements, initialized to 0.
211  error_per_cell.clear();
212  error_per_cell.resize (mesh.max_elem_id(), 0.);
213 
214 #ifndef NDEBUG
215  // These variables are only used in assertions later so
216  // avoid declaring them unless asserts are active.
217  const dof_id_type n_coarse_elem = mesh.n_active_elem();
218 
219  dof_id_type local_dof_bearing_nodes = 0;
220  const unsigned int sysnum = system.number();
221  for (const auto * node : mesh.local_node_ptr_range())
222  for (unsigned int v=0, nvars = node->n_vars(sysnum);
223  v != nvars; ++v)
224  if (node->n_comp(sysnum, v))
225  {
226  local_dof_bearing_nodes++;
227  break;
228  }
229 #endif // NDEBUG
230 
231  // Uniformly refine the mesh
232  MeshRefinement mesh_refinement(mesh);
233 
235 
236  // FIXME: this may break if there is more than one System
237  // on this mesh but estimate_error was still called instead of
238  // estimate_errors
239  for (unsigned int i = 0; i != number_h_refinements; ++i)
240  {
241  mesh_refinement.uniformly_refine(1);
242  es.reinit();
243  }
244 
245  for (unsigned int i = 0; i != number_p_refinements; ++i)
246  {
247  mesh_refinement.uniformly_p_refine(1);
248  es.reinit();
249  }
250 
251  // Copy the projected coarse grid solutions, which will be
252  // overwritten by solve()
253  std::vector<std::unique_ptr<NumericVector<Number>>> coarse_adjoints;
254  for (auto j : IntRange<unsigned int>(0, system.n_qois()))
255  {
256  if (_qoi_set.has_index(j))
257  {
258  auto coarse_adjoint = NumericVector<Number>::build(mesh.comm());
259 
260  // Can do "fast" init since we're overwriting this in a sec
261  coarse_adjoint->init(system.get_adjoint_solution(j),
262  /* fast = */ true);
263 
264  *coarse_adjoint = system.get_adjoint_solution(j);
265 
266  coarse_adjoints.emplace_back(std::move(coarse_adjoint));
267  }
268  else
269  coarse_adjoints.emplace_back(nullptr);
270  }
271 
272  // Next, we are going to build up the residual for evaluating the
273  // error estimate.
274 
275  // If the residual physics pointer is not null, use it when
276  // evaluating here.
277  {
278  const bool swapping_physics = _residual_evaluation_physics;
279  if (swapping_physics)
280  dynamic_cast<DifferentiableSystem &>(system).swap_physics(_residual_evaluation_physics);
281 
282  // Rebuild the rhs with the projected primal solution, constraints
283  // have to be applied to get the correct error estimate since error
284  // on the Dirichlet boundary is zero
285  (dynamic_cast<ImplicitSystem &>(system)).assembly(true, false);
286 
287  // Swap back if needed
288  if (swapping_physics)
289  dynamic_cast<DifferentiableSystem &>(system).swap_physics(_residual_evaluation_physics);
290  }
291 
292  NumericVector<Number> & projected_residual = (dynamic_cast<ExplicitSystem &>(system)).get_vector("RHS Vector");
293  projected_residual.close();
294 
295  // Solve the adjoint problem(s) on the refined FE space
296  system.adjoint_solve(_qoi_set);
297 
298  // Now that we have the refined adjoint solution and the projected primal solution,
299  // we first compute the global QoI error estimate
300 
301  // Resize the computed_global_QoI_errors vector to hold the error estimates for each QoI
302  computed_global_QoI_errors.resize(system.n_qois());
303 
304  // Loop over all the adjoint solutions and get the QoI error
305  // contributions from all of them. While we're looping anyway we'll
306  // pull off the coarse adjoints
307  for (auto j : IntRange<unsigned int>(0, system.n_qois()))
308  {
309  // Skip this QoI if not in the QoI Set
310  if (_qoi_set.has_index(j))
311  {
312 
313  // If the adjoint solution has heterogeneous dirichlet
314  // values, then to get a proper error estimate here we need
315  // to subtract off a coarse grid lift function.
316  // |Q(u) - Q(u^h)| = |R([u^h]+, z^h+ - [L]+)| + HOT
317  if(system.get_dof_map().has_adjoint_dirichlet_boundaries(j))
318  {
319  // Need to create a string with current loop index to retrieve
320  // the correct vector from the liftvectors map
321  std::ostringstream liftfunc_name;
322  liftfunc_name << "adjoint_lift_function" << j;
323 
324  // Subtract off the corresponding lift vector from the adjoint solution
325  system.get_adjoint_solution(j) -= system.get_vector(liftfunc_name.str());
326 
327  // Now evaluate R(u^h, z^h+ - lift)
328  computed_global_QoI_errors[j] = projected_residual.dot(system.get_adjoint_solution(j));
329 
330  // Add the lift back to get back the adjoint
331  system.get_adjoint_solution(j) += system.get_vector(liftfunc_name.str());
332 
333  }
334  else
335  {
336  // Usual dual weighted residual error estimate
337  // |Q(u) - Q(u^h)| = |R([u^h]+, z^h+)| + HOT
338  computed_global_QoI_errors[j] = projected_residual.dot(system.get_adjoint_solution(j));
339  }
340 
341  }
342  }
343 
344  // Done with the global error estimates, now construct the element wise error indicators
345 
346  // To get a better element wise breakdown of the error estimate,
347  // we subtract off a coarse representation of the adjoint solution.
348  // |Q(u) - Q(u^h)| = |R([u^h]+, z^h+ - [z^h]+)|
349  // This remains valid for all combinations of heterogenous adjoint bcs and
350  // stabilized/non-stabilized formulations, except for the case where we not using a
351  // heterogenous adjoint bc and have a stabilized formulation.
352  // Then, R(u^h_s, z^h_s) != 0 (no Galerkin orthogonality w.r.t the non-stabilized residual)
353  for (auto j : IntRange<unsigned int>(0, system.n_qois()))
354  {
355  // Skip this QoI if not in the QoI Set
356  if (_qoi_set.has_index(j))
357  {
358  // If we have a nullptr residual evaluation physics pointer, we
359  // assume the user's formulation is consistent from mesh to
360  // mesh, so we have Galerkin orthogonality and we can get
361  // better indicator results by subtracting a coarse adjoint.
362 
363  // If we have a residual evaluation physics pointer, but we
364  // also have heterogeneous adjoint dirichlet boundaries,
365  // then we have to subtract off *some* lift function for
366  // consistency, and we choose the coarse adjoint in lieu of
367  // having any better ideas.
368 
369  // If we have a residual evaluation physics pointer and we
370  // have homogeneous adjoint dirichlet boundaries, then we
371  // don't have to subtract off anything, and with stabilized
372  // formulations we get the best results if we don't.
373  if(system.get_dof_map().has_adjoint_dirichlet_boundaries(j)
375  {
376  // z^h+ -> z^h+ - [z^h]+
377  system.get_adjoint_solution(j) -= *coarse_adjoints[j];
378  }
379  }
380  }
381 
382  // We ought to account for 'spill-over' effects while computing the
383  // element error indicators This happens because the same dof is
384  // shared by multiple elements, one way of mitigating this is to
385  // scale the contribution from each dof by the number of elements it
386  // belongs to We first obtain the number of elements each node
387  // belongs to
388 
389  // A map that relates a node id to an int that will tell us how many elements it is a node of
390  std::unordered_map<dof_id_type, unsigned int> shared_element_count;
391 
392  // To fill this map, we will loop over elements, and then in each element, we will loop
393  // over the nodes each element contains, and then query it for the number of coarse
394  // grid elements it was a node of
395 
396  // Keep track of which nodes we have already dealt with
397  std::unordered_set<dof_id_type> processed_node_ids;
398 
399  // We will be iterating over all the active elements in the fine mesh that live on
400  // this processor.
401  for (const auto & elem : mesh.active_local_element_ptr_range())
402  for (const Node & node : elem->node_ref_range())
403  {
404  // Get the id of this node
405  dof_id_type node_id = node.id();
406 
407  // If we havent already processed this node, do so now
408  if (processed_node_ids.find(node_id) == processed_node_ids.end())
409  {
410  // Declare a neighbor_set to be filled by the find_point_neighbors
411  std::set<const Elem *> fine_grid_neighbor_set;
412 
413  // Call find_point_neighbors to fill the neighbor_set
414  elem->find_point_neighbors(node, fine_grid_neighbor_set);
415 
416  // A vector to hold the coarse grid parents neighbors
417  std::vector<dof_id_type> coarse_grid_neighbors;
418 
419  // Loop over all the fine neighbors of this node
420  for (const auto & fine_elem : fine_grid_neighbor_set)
421  {
422  // Find the element id for the corresponding coarse grid element
423  const Elem * coarse_elem = fine_elem;
424  for (unsigned int j = 0; j != number_h_refinements; ++j)
425  {
426  libmesh_assert (coarse_elem->parent());
427 
428  coarse_elem = coarse_elem->parent();
429  }
430 
431  // Loop over the existing coarse neighbors and check if this one is
432  // already in there
433  const dof_id_type coarse_id = coarse_elem->id();
434  std::size_t j = 0;
435 
436  // If the set already contains this element break out of the loop
437  for (std::size_t cgns = coarse_grid_neighbors.size(); j != cgns; j++)
438  if (coarse_grid_neighbors[j] == coarse_id)
439  break;
440 
441  // If we didn't leave the loop even at the last element,
442  // this is a new neighbour, put in the coarse_grid_neighbor_set
443  if (j == coarse_grid_neighbors.size())
444  coarse_grid_neighbors.push_back(coarse_id);
445  } // End loop over fine neighbors
446 
447  // Set the shared_neighbour index for this node to the
448  // size of the coarse grid neighbor set
449  shared_element_count[node_id] =
450  cast_int<unsigned int>(coarse_grid_neighbors.size());
451 
452  // Add this node to processed_node_ids vector
453  processed_node_ids.insert(node_id);
454  } // End if not processed node
455  } // End loop over nodes
456 
457  // Get a DoF map, will be used to get the nodal dof_indices for each element
458  DofMap & dof_map = system.get_dof_map();
459 
460  // The global DOF indices, we will use these later on when we compute the element wise indicators
461  std::vector<dof_id_type> dof_indices;
462 
463  // Localize the global rhs and adjoint solution vectors (which might be shared on multiple processors) onto a
464  // local ghosted vector, this ensures each processor has all the dof_indices to compute an error indicator for
465  // an element it owns
466  std::unique_ptr<NumericVector<Number>> localized_projected_residual = NumericVector<Number>::build(system.comm());
467  localized_projected_residual->init(system.n_dofs(), system.n_local_dofs(), system.get_dof_map().get_send_list(), false, GHOSTED);
468  projected_residual.localize(*localized_projected_residual, system.get_dof_map().get_send_list());
469 
470  // Each adjoint solution will also require ghosting; for efficiency we'll reuse the same memory
471  std::unique_ptr<NumericVector<Number>> localized_adjoint_solution = NumericVector<Number>::build(system.comm());
472  localized_adjoint_solution->init(system.n_dofs(), system.n_local_dofs(), system.get_dof_map().get_send_list(), false, GHOSTED);
473 
474  // We will loop over each adjoint solution, localize that adjoint
475  // solution and then loop over local elements
476  for (auto i : IntRange<unsigned int>(0, system.n_qois()))
477  {
478  // Skip this QoI if not in the QoI Set
479  if (_qoi_set.has_index(i))
480  {
481  // Get the weight for the current QoI
482  Real error_weight = _qoi_set.weight(i);
483 
484  (system.get_adjoint_solution(i)).localize(*localized_adjoint_solution, system.get_dof_map().get_send_list());
485 
486  // Loop over elements
487  for (const auto & elem : mesh.active_local_element_ptr_range())
488  {
489  // Go up number_h_refinements levels up to find the coarse parent
490  const Elem * coarse = elem;
491 
492  for (unsigned int j = 0; j != number_h_refinements; ++j)
493  {
494  libmesh_assert (coarse->parent());
495 
496  coarse = coarse->parent();
497  }
498 
499  const dof_id_type e_id = coarse->id();
500 
501  // Get the local to global degree of freedom maps for this element
502  dof_map.dof_indices (elem, dof_indices);
503 
504  // We will have to manually do the dot products.
505  Number local_contribution = 0.;
506 
507  // Sum the contribution to the error indicator for each element from the current QoI
508  for (const auto & dof : dof_indices)
509  local_contribution += (*localized_projected_residual)(dof) * (*localized_adjoint_solution)(dof);
510 
511  // Multiply by the error weight for this QoI
512  local_contribution *= error_weight;
513 
514  // FIXME: we're throwing away information in the
515  // --enable-complex case
516  error_per_cell[e_id] += static_cast<ErrorVectorReal>
517  (std::abs(local_contribution));
518 
519  } // End loop over elements
520  } // End if belong to QoI set
521  } // End loop over QoIs
522 
523  // Don't bother projecting the solution; we'll restore from backup
524  // after coarsening
525  system.project_solution_on_reinit() = false;
526 
527  // Uniformly coarsen the mesh, without projecting the solution
529 
530  for (unsigned int i = 0; i != number_h_refinements; ++i)
531  {
532  mesh_refinement.uniformly_coarsen(1);
533  // FIXME - should the reinits here be necessary? - RHS
534  es.reinit();
535  }
536 
537  for (unsigned int i = 0; i != number_p_refinements; ++i)
538  {
539  mesh_refinement.uniformly_p_coarsen(1);
540  es.reinit();
541  }
542 
543  // We should have the same number of active elements as when we started,
544  // but not necessarily the same number of elements since reinit() doesn't
545  // always call contract()
546  libmesh_assert_equal_to (n_coarse_elem, mesh.n_active_elem());
547 
548  // We should have the same number of dof-bearing nodes as when we
549  // started
550 #ifndef NDEBUG
551  dof_id_type final_local_dof_bearing_nodes = 0;
552  for (const auto * node : mesh.local_node_ptr_range())
553  for (unsigned int v=0, nvars = node->n_vars(sysnum);
554  v != nvars; ++v)
555  if (node->n_comp(sysnum, v))
556  {
557  final_local_dof_bearing_nodes++;
558  break;
559  }
560  libmesh_assert_equal_to (local_dof_bearing_nodes,
561  final_local_dof_bearing_nodes);
562 #endif // NDEBUG
563 
564  // Restore old solutions and clean up the heap
565  system.project_solution_on_reinit() = old_projection_setting;
566 
567  // Restore the coarse solution vectors and delete their copies
568  *system.solution = *coarse_solution;
569  *system.current_local_solution = *coarse_local_solution;
570 
571  for (const auto & pr : as_range(system.vectors_begin(), system.vectors_end()))
572  {
573  // The (string) name of this vector
574  const std::string & var_name = pr.first;
575 
576  // If it's a vector we already had (and not a newly created
577  // vector like an adjoint rhs), we need to restore it.
578  auto it = coarse_vectors.find(var_name);
579  if (it != coarse_vectors.end())
580  {
581  NumericVector<Number> * coarsevec = it->second.get();
582  system.get_vector(var_name) = *coarsevec;
583 
584  coarsevec->clear();
585  }
586  }
587 
588  // Restore old partitioner and renumbering settings
589  mesh.partitioner().reset(old_partitioner.release());
590  mesh.allow_renumbering(old_renumbering_setting);
591 
592  // Finally sum the vector of estimated error values.
593  this->reduce_error(error_per_cell, system.comm());
594 
595  // We don't take a square root here; this is a goal-oriented
596  // estimate not a Hilbert norm estimate.
597 } // end estimate_error function

References _qoi_set, _residual_evaluation_physics, std::abs(), libMesh::System::add_vector(), libMesh::System::adjoint_solve(), libMesh::as_range(), libMesh::NumericVector< T >::build(), libMesh::NumericVector< T >::clear(), libMesh::NumericVector< T >::close(), libMesh::ParallelObject::comm(), computed_global_QoI_errors, libMesh::System::current_local_solution, libMesh::DofMap::dof_indices(), libMesh::NumericVector< T >::dot(), libMesh::DofMap::enforce_adjoint_constraints_exactly(), libMesh::NumericVector< T >::get(), libMesh::System::get_adjoint_solution(), libMesh::System::get_dof_map(), libMesh::System::get_equation_systems(), libMesh::EquationSystems::get_mesh(), libMesh::DofMap::get_send_list(), libMesh::System::get_vector(), libMesh::GHOSTED, libMesh::DofMap::has_adjoint_dirichlet_boundaries(), libMesh::QoISet::has_index(), libMesh::DofObject::id(), libMesh::TriangleWrapper::init(), libMesh::System::is_adjoint_already_solved(), libMesh::libmesh_assert(), libMesh::NumericVector< T >::localize(), mesh, libMesh::System::n_dofs(), libMesh::System::n_local_dofs(), libMesh::System::n_qois(), libMesh::System::number(), number_h_refinements, number_p_refinements, libMesh::Elem::parent(), libMesh::System::project_solution_on_reinit(), libMesh::Real, libMesh::ErrorEstimator::reduce_error(), libMesh::EquationSystems::reinit(), libMesh::System::solution, libMesh::NumericVector< T >::swap(), libMesh::MeshRefinement::uniformly_coarsen(), libMesh::MeshRefinement::uniformly_p_coarsen(), libMesh::MeshRefinement::uniformly_p_refine(), libMesh::MeshRefinement::uniformly_refine(), libMesh::System::update(), libMesh::System::vectors_begin(), libMesh::System::vectors_end(), and libMesh::QoISet::weight().

◆ estimate_errors() [1/2]

void libMesh::ErrorEstimator::estimate_errors ( const EquationSystems equation_systems,
ErrorMap errors_per_cell,
const std::map< const System *, const NumericVector< Number > * > *  solution_vectors = nullptr,
bool  estimate_parent_error = false 
)
virtualinherited

This virtual function can be redefined in derived classes, but by default it calls estimate_error repeatedly to calculate the requested error vectors.

FIXME: This is a default implementation - derived classes should reimplement it for efficiency.

Currently this function ignores the error_norm.weight() values because it calculates each variable's error individually, unscaled.

The user selects which errors get computed by filling a map with error vectors: If errors_per_cell[&system][v] exists, it will be filled with the error values in variable v of system

Reimplemented in libMesh::UniformRefinementEstimator.

Definition at line 93 of file error_estimator.C.

97 {
98  SystemNorm old_error_norm = this->error_norm;
99 
100  // Find the requested error values from each system
101  for (auto s : IntRange<unsigned int>(0, equation_systems.n_systems()))
102  {
103  const System & sys = equation_systems.get_system(s);
104 
105  unsigned int n_vars = sys.n_vars();
106 
107  for (unsigned int v = 0; v != n_vars; ++v)
108  {
109  // Only fill in ErrorVectors the user asks for
110  if (errors_per_cell.find(std::make_pair(&sys, v)) ==
111  errors_per_cell.end())
112  continue;
113 
114  // Calculate error in only one variable
115  std::vector<Real> weights(n_vars, 0.0);
116  weights[v] = 1.0;
117  this->error_norm =
118  SystemNorm(std::vector<FEMNormType>(n_vars, old_error_norm.type(v)),
119  weights);
120 
121  const NumericVector<Number> * solution_vector = nullptr;
122  if (solution_vectors &&
123  solution_vectors->find(&sys) != solution_vectors->end())
124  solution_vector = solution_vectors->find(&sys)->second;
125 
126  this->estimate_error
127  (sys, *errors_per_cell[std::make_pair(&sys, v)],
128  solution_vector, estimate_parent_error);
129  }
130  }
131 
132  // Restore our old state before returning
133  this->error_norm = old_error_norm;
134 }

References libMesh::ErrorEstimator::error_norm, libMesh::ErrorEstimator::estimate_error(), libMesh::EquationSystems::get_system(), libMesh::EquationSystems::n_systems(), n_vars, libMesh::System::n_vars(), and libMesh::SystemNorm::type().

◆ estimate_errors() [2/2]

void libMesh::ErrorEstimator::estimate_errors ( const EquationSystems equation_systems,
ErrorVector error_per_cell,
const std::map< const System *, SystemNorm > &  error_norms,
const std::map< const System *, const NumericVector< Number > * > *  solution_vectors = nullptr,
bool  estimate_parent_error = false 
)
virtualinherited

This virtual function can be redefined in derived classes, but by default computes the sum of the error_per_cell for each system in the equation_systems.

Currently this function ignores the error_norm member variable, and uses the function argument error_norms instead.

This function is named estimate_errors instead of estimate_error because otherwise C++ can get confused.

Reimplemented in libMesh::UniformRefinementEstimator.

Definition at line 47 of file error_estimator.C.

52 {
53  SystemNorm old_error_norm = this->error_norm;
54 
55  // Sum the error values from each system
56  for (auto s : IntRange<unsigned int>(0, equation_systems.n_systems()))
57  {
58  ErrorVector system_error_per_cell;
59  const System & sys = equation_systems.get_system(s);
60  if (error_norms.find(&sys) == error_norms.end())
61  this->error_norm = old_error_norm;
62  else
63  this->error_norm = error_norms.find(&sys)->second;
64 
65  const NumericVector<Number> * solution_vector = nullptr;
66  if (solution_vectors &&
67  solution_vectors->find(&sys) != solution_vectors->end())
68  solution_vector = solution_vectors->find(&sys)->second;
69 
70  this->estimate_error(sys, system_error_per_cell,
71  solution_vector, estimate_parent_error);
72 
73  if (s)
74  {
75  libmesh_assert_equal_to (error_per_cell.size(), system_error_per_cell.size());
76  for (auto i : index_range(error_per_cell))
77  error_per_cell[i] += system_error_per_cell[i];
78  }
79  else
80  error_per_cell = system_error_per_cell;
81  }
82 
83  // Restore our old state before returning
84  this->error_norm = old_error_norm;
85 }

References libMesh::ErrorEstimator::error_norm, libMesh::ErrorEstimator::estimate_error(), libMesh::EquationSystems::get_system(), libMesh::index_range(), and libMesh::EquationSystems::n_systems().

◆ get_global_QoI_error_estimate()

Number& libMesh::AdjointRefinementEstimator::get_global_QoI_error_estimate ( unsigned int  qoi_index)
inline

This is an accessor function to access the computed global QoI error estimates.

Definition at line 106 of file adjoint_refinement_estimator.h.

107  {
108  return computed_global_QoI_errors[qoi_index];
109  }

References computed_global_QoI_errors.

◆ get_residual_evaluation_physics()

DifferentiablePhysics* libMesh::AdjointRefinementEstimator::get_residual_evaluation_physics ( )
inline
Returns
A pointer to the DifferentiablePhysics object or nullptr if no external Physics object is attached.

Definition at line 127 of file adjoint_refinement_estimator.h.

128  { return this->_residual_evaluation_physics; }

References _residual_evaluation_physics.

◆ operator=() [1/2]

AdjointRefinementEstimator& libMesh::AdjointRefinementEstimator::operator= ( AdjointRefinementEstimator &&  )
default

◆ operator=() [2/2]

AdjointRefinementEstimator& libMesh::AdjointRefinementEstimator::operator= ( const AdjointRefinementEstimator )
default

◆ qoi_set() [1/2]

QoISet& libMesh::AdjointRefinementEstimator::qoi_set ( )
inline

Access to the QoISet (default: weight all QoIs equally) to use when computing errors.

Definition at line 73 of file adjoint_refinement_estimator.h.

73 { return _qoi_set; }

References _qoi_set.

Referenced by build_adjoint_refinement_error_estimator().

◆ qoi_set() [2/2]

const QoISet& libMesh::AdjointRefinementEstimator::qoi_set ( ) const
inline

Access to the QoISet (default: weight all QoIs equally) to use when computing errors.

Definition at line 79 of file adjoint_refinement_estimator.h.

79 { return _qoi_set; }

References _qoi_set.

◆ reduce_error()

void libMesh::ErrorEstimator::reduce_error ( std::vector< ErrorVectorReal > &  error_per_cell,
const Parallel::Communicator &  comm 
) const
protectedinherited

This method takes the local error contributions in error_per_cell from each processor and combines them to get the global error vector.

Definition at line 32 of file error_estimator.C.

34 {
35  // This function must be run on all processors at once
36  // parallel_object_only();
37 
38  // Each processor has now computed the error contributions
39  // for its local elements. We may need to sum the vector to
40  // recover the error for each element.
41 
42  comm.sum(error_per_cell);
43 }

Referenced by libMesh::UniformRefinementEstimator::_estimate_error(), libMesh::WeightedPatchRecoveryErrorEstimator::estimate_error(), libMesh::PatchRecoveryErrorEstimator::estimate_error(), libMesh::JumpErrorEstimator::estimate_error(), estimate_error(), and libMesh::ExactErrorEstimator::estimate_error().

◆ set_residual_evaluation_physics()

void libMesh::AdjointRefinementEstimator::set_residual_evaluation_physics ( DifferentiablePhysics set_physics)
inline

Set the _residual_evaluation_physics member to argument.

Definition at line 133 of file adjoint_refinement_estimator.h.

134  { this->_residual_evaluation_physics = set_physics; }

References _residual_evaluation_physics.

◆ type()

ErrorEstimatorType libMesh::AdjointRefinementEstimator::type ( ) const
virtual
Returns
The type for the ErrorEstimator subclass.

Implements libMesh::ErrorEstimator.

Definition at line 85 of file adjoint_refinement_estimator.C.

86 {
87  return ADJOINT_REFINEMENT;
88 }

References libMesh::ADJOINT_REFINEMENT.

Member Data Documentation

◆ _qoi_set

QoISet libMesh::AdjointRefinementEstimator::_qoi_set
protected

A QoISet to handle cases with multiple QoIs available.

Definition at line 150 of file adjoint_refinement_estimator.h.

Referenced by estimate_error(), and qoi_set().

◆ _residual_evaluation_physics

DifferentiablePhysics* libMesh::AdjointRefinementEstimator::_residual_evaluation_physics
protected

Pointer to object to use for physics assembly evaluations.

Defaults to nullptr for backwards compatibility.

Definition at line 142 of file adjoint_refinement_estimator.h.

Referenced by estimate_error(), get_residual_evaluation_physics(), and set_residual_evaluation_physics().

◆ computed_global_QoI_errors

std::vector<Number> libMesh::AdjointRefinementEstimator::computed_global_QoI_errors
protected

◆ error_norm

SystemNorm libMesh::ErrorEstimator::error_norm
inherited

When estimating the error in a single system, the error_norm is used to control the scaling and norm choice for each variable.

Not all estimators will support all norm choices. The default scaling is for all variables to be weighted equally. The default norm choice depends on the error estimator.

Part of this functionality was supported via component_scale and sobolev_order in older libMesh versions, and a small part was supported via component_mask in even older versions. Hopefully the encapsulation here will allow us to avoid changing this API again.

Definition at line 164 of file error_estimator.h.

Referenced by libMesh::UniformRefinementEstimator::_estimate_error(), AdjointRefinementEstimator(), libMesh::DiscontinuityMeasure::boundary_side_integration(), libMesh::KellyErrorEstimator::boundary_side_integration(), libMesh::DiscontinuityMeasure::DiscontinuityMeasure(), libMesh::JumpErrorEstimator::estimate_error(), libMesh::AdjointResidualErrorEstimator::estimate_error(), libMesh::ExactErrorEstimator::estimate_error(), libMesh::ErrorEstimator::estimate_errors(), libMesh::ExactErrorEstimator::ExactErrorEstimator(), libMesh::ExactErrorEstimator::find_squared_element_error(), libMesh::LaplacianErrorEstimator::init_context(), libMesh::DiscontinuityMeasure::init_context(), libMesh::KellyErrorEstimator::init_context(), libMesh::LaplacianErrorEstimator::internal_side_integration(), libMesh::DiscontinuityMeasure::internal_side_integration(), libMesh::KellyErrorEstimator::internal_side_integration(), libMesh::KellyErrorEstimator::KellyErrorEstimator(), libMesh::LaplacianErrorEstimator::LaplacianErrorEstimator(), main(), libMesh::WeightedPatchRecoveryErrorEstimator::EstimateError::operator()(), libMesh::PatchRecoveryErrorEstimator::EstimateError::operator()(), libMesh::PatchRecoveryErrorEstimator::PatchRecoveryErrorEstimator(), libMesh::JumpErrorEstimator::reinit_sides(), and libMesh::UniformRefinementEstimator::UniformRefinementEstimator().

◆ number_h_refinements

unsigned char libMesh::AdjointRefinementEstimator::number_h_refinements

How many h refinements to perform to get the fine grid.

Definition at line 116 of file adjoint_refinement_estimator.h.

Referenced by build_adjoint_refinement_error_estimator(), and estimate_error().

◆ number_p_refinements

unsigned char libMesh::AdjointRefinementEstimator::number_p_refinements

How many p refinements to perform to get the fine grid.

Definition at line 121 of file adjoint_refinement_estimator.h.

Referenced by estimate_error().


The documentation for this class was generated from the following files:
libMesh::Number
Real Number
Definition: libmesh_common.h:195
libMesh::dof_id_type
uint8_t dof_id_type
Definition: id_types.h:67
libMesh::ErrorEstimator::error_norm
SystemNorm error_norm
When estimating the error in a single system, the error_norm is used to control the scaling and norm ...
Definition: error_estimator.h:164
n_vars
unsigned int n_vars
Definition: adaptivity_ex3.C:116
libMesh::AdjointRefinementEstimator::computed_global_QoI_errors
std::vector< Number > computed_global_QoI_errors
Definition: adjoint_refinement_estimator.h:145
libMesh::QoISet::has_index
bool has_index(std::size_t) const
Return whether or not this index is in the set to be calculated.
Definition: qoi_set.h:221
libMesh::AdjointRefinementEstimator::_qoi_set
QoISet _qoi_set
A QoISet to handle cases with multiple QoIs available.
Definition: adjoint_refinement_estimator.h:150
libMesh::index_range
IntRange< std::size_t > index_range(const std::vector< T > &vec)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition: int_range.h:106
libMesh::AdjointRefinementEstimator::_residual_evaluation_physics
DifferentiablePhysics * _residual_evaluation_physics
Pointer to object to use for physics assembly evaluations.
Definition: adjoint_refinement_estimator.h:142
libMesh::GHOSTED
Definition: enum_parallel_type.h:37
mesh
MeshBase & mesh
Definition: mesh_communication.C:1257
libMesh::NumericVector::build
static std::unique_ptr< NumericVector< T > > build(const Parallel::Communicator &comm, const SolverPackage solver_package=libMesh::default_solver_package())
Builds a NumericVector on the processors in communicator comm using the linear solver package specifi...
Definition: numeric_vector.C:49
libMesh::TriangleWrapper::init
void init(triangulateio &t)
Initializes the fields of t to nullptr/0 as necessary.
libMesh::libmesh_assert
libmesh_assert(ctx)
libMesh::ErrorEstimator::estimate_error
virtual void estimate_error(const System &system, ErrorVector &error_per_cell, const NumericVector< Number > *solution_vector=nullptr, bool estimate_parent_error=false)=0
This pure virtual function must be redefined in derived classes to compute the error for each active ...
std::abs
MetaPhysicL::DualNumber< T, D > abs(const MetaPhysicL::DualNumber< T, D > &in)
libMesh::AdjointRefinementEstimator::number_h_refinements
unsigned char number_h_refinements
How many h refinements to perform to get the fine grid.
Definition: adjoint_refinement_estimator.h:116
libMesh::ErrorEstimator::ErrorEstimator
ErrorEstimator()=default
Constructor.
libMesh::AdjointRefinementEstimator::number_p_refinements
unsigned char number_p_refinements
How many p refinements to perform to get the fine grid.
Definition: adjoint_refinement_estimator.h:121
libMesh::as_range
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
Helper function that allows us to treat a homogenous pair as a range.
Definition: simple_range.h:57
libMesh::QoISet::weight
Real weight(std::size_t) const
Get the weight for this index (default 1.0)
Definition: qoi_set.h:240
libMesh::ErrorEstimator::reduce_error
void reduce_error(std::vector< ErrorVectorReal > &error_per_cell, const Parallel::Communicator &comm) const
This method takes the local error contributions in error_per_cell from each processor and combines th...
Definition: error_estimator.C:32
libMesh::ADJOINT_REFINEMENT
Definition: enum_error_estimator_type.h:35
libMesh::INVALID_NORM
Definition: enum_norm_type.h:61
libMesh::Real
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
Definition: libmesh_common.h:121