libMesh
jump_error_estimator.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2019 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 // C++ includes
20 #include <algorithm> // for std::fill
21 #include <cstdlib> // *must* precede <cmath> for proper std:abs() on PGI, Sun Studio CC
22 #include <cmath> // for sqrt
23 
24 // Local Includes
25 #include "libmesh/libmesh_common.h"
26 #include "libmesh/jump_error_estimator.h"
27 #include "libmesh/dof_map.h"
28 #include "libmesh/elem.h"
29 #include "libmesh/error_vector.h"
30 #include "libmesh/fe_base.h"
31 #include "libmesh/fe_interface.h"
32 #include "libmesh/fem_context.h"
33 #include "libmesh/libmesh_logging.h"
34 #include "libmesh/mesh_base.h"
35 #include "libmesh/quadrature_gauss.h"
36 #include "libmesh/system.h"
37 #include "libmesh/dense_vector.h"
38 #include "libmesh/numeric_vector.h"
39 #include "libmesh/int_range.h"
40 #include "libmesh/auto_ptr.h" // libmesh_make_unique
41 
42 namespace libMesh
43 {
44 
45 //-----------------------------------------------------------------
46 // JumpErrorEstimator implementations
48 {
49 }
50 
51 
52 
54  ErrorVector & error_per_cell,
55  const NumericVector<Number> * solution_vector,
56  bool estimate_parent_error)
57 {
58  LOG_SCOPE("estimate_error()", "JumpErrorEstimator");
59 
96  // This parameter is not used when !LIBMESH_ENABLE_AMR.
97  libmesh_ignore(estimate_parent_error);
98 
99  // The current mesh
100  const MeshBase & mesh = system.get_mesh();
101 
102  // The number of variables in the system
103  const unsigned int n_vars = system.n_vars();
104 
105  // The DofMap for this system
106 #ifdef LIBMESH_ENABLE_AMR
107  const DofMap & dof_map = system.get_dof_map();
108 #endif
109 
110  // Resize the error_per_cell vector to be
111  // the number of elements, initialize it to 0.
112  error_per_cell.resize (mesh.max_elem_id());
113  std::fill (error_per_cell.begin(), error_per_cell.end(), 0.);
114 
115  // Declare a vector of floats which is as long as
116  // error_per_cell above, and fill with zeros. This vector will be
117  // used to keep track of the number of edges (faces) on each active
118  // element which are either:
119  // 1) an internal edge
120  // 2) an edge on a Neumann boundary for which a boundary condition
121  // function has been specified.
122  // The error estimator can be scaled by the number of flux edges (faces)
123  // which the element actually has to obtain a more uniform measure
124  // of the error. Use floats instead of ints since in case 2 (above)
125  // f gets 1/2 of a flux face contribution from each of his
126  // neighbors
127  std::vector<float> n_flux_faces;
129  n_flux_faces.resize(error_per_cell.size(), 0);
130 
131  // Prepare current_local_solution to localize a non-standard
132  // solution vector if necessary
133  if (solution_vector && solution_vector != system.solution.get())
134  {
135  NumericVector<Number> * newsol =
136  const_cast<NumericVector<Number> *>(solution_vector);
137  System & sys = const_cast<System &>(system);
138  newsol->swap(*sys.solution);
139  sys.update();
140  }
141 
142  fine_context = libmesh_make_unique<FEMContext>(system);
143  coarse_context = libmesh_make_unique<FEMContext>(system);
144 
145  // Don't overintegrate - we're evaluating differences of FE values,
146  // not products of them.
148  fine_context->use_unweighted_quadrature_rules(system.extra_quadrature_order);
149 
150  // Loop over all the variables we've been requested to find jumps in, to
151  // pre-request
152  for (var=0; var<n_vars; var++)
153  {
154  // Skip variables which aren't part of our norm,
155  // as well as SCALAR variables, which have no jumps
156  if (error_norm.weight(var) == 0.0 ||
157  system.variable_type(var).family == SCALAR)
158  continue;
159 
160  // FIXME: Need to generalize this to vector-valued elements. [PB]
161  FEBase * side_fe = nullptr;
162 
163  const std::set<unsigned char> & elem_dims =
164  fine_context->elem_dimensions();
165 
166  for (const auto & dim : elem_dims)
167  {
168  fine_context->get_side_fe( var, side_fe, dim );
169 
170  side_fe->get_xyz();
171  }
172  }
173 
174  this->init_context(*fine_context);
176 
177  // Iterate over all the active elements in the mesh
178  // that live on this processor.
179  for (const auto & e : mesh.active_local_element_ptr_range())
180  {
181  const dof_id_type e_id = e->id();
182 
183 #ifdef LIBMESH_ENABLE_AMR
184  // See if the parent of element e has been examined yet;
185  // if not, we may want to compute the estimator on it
186  const Elem * parent = e->parent();
187 
188  // We only can compute and only need to compute on
189  // parents with all active children
190  bool compute_on_parent = true;
191  if (!parent || !estimate_parent_error)
192  compute_on_parent = false;
193  else
194  for (auto & child : parent->child_ref_range())
195  if (!child.active())
196  compute_on_parent = false;
197 
198  if (compute_on_parent &&
199  !error_per_cell[parent->id()])
200  {
201  // Compute a projection onto the parent
202  DenseVector<Number> Uparent;
204  (*(system.solution), dof_map, parent, Uparent, false);
205 
206  // Loop over the neighbors of the parent
207  for (auto n_p : parent->side_index_range())
208  {
209  if (parent->neighbor_ptr(n_p) != nullptr) // parent has a neighbor here
210  {
211  // Find the active neighbors in this direction
212  std::vector<const Elem *> active_neighbors;
213  parent->neighbor_ptr(n_p)->
214  active_family_tree_by_neighbor(active_neighbors,
215  parent);
216  // Compute the flux to each active neighbor
217  for (std::size_t a=0,
218  n_active_neighbors = active_neighbors.size();
219  a != n_active_neighbors; ++a)
220  {
221  const Elem * f = active_neighbors[a];
222  // FIXME - what about when f->level <
223  // parent->level()??
224  if (f->level() >= parent->level())
225  {
226  fine_context->pre_fe_reinit(system, f);
227  coarse_context->pre_fe_reinit(system, parent);
228  libmesh_assert_equal_to
229  (coarse_context->get_elem_solution().size(),
230  Uparent.size());
231  coarse_context->get_elem_solution() = Uparent;
232 
233  this->reinit_sides();
234 
235  // Loop over all significant variables in the system
236  for (var=0; var<n_vars; var++)
237  if (error_norm.weight(var) != 0.0 &&
238  system.variable_type(var).family != SCALAR)
239  {
241 
242  error_per_cell[fine_context->get_elem().id()] +=
243  static_cast<ErrorVectorReal>(fine_error);
244  error_per_cell[coarse_context->get_elem().id()] +=
245  static_cast<ErrorVectorReal>(coarse_error);
246  }
247 
248  // Keep track of the number of internal flux
249  // sides found on each element
251  {
252  n_flux_faces[fine_context->get_elem().id()]++;
253  n_flux_faces[coarse_context->get_elem().id()] +=
255  }
256  }
257  }
258  }
259  else if (integrate_boundary_sides)
260  {
261  fine_context->pre_fe_reinit(system, parent);
262  libmesh_assert_equal_to
263  (fine_context->get_elem_solution().size(),
264  Uparent.size());
265  fine_context->get_elem_solution() = Uparent;
266  fine_context->side = cast_int<unsigned char>(n_p);
267  fine_context->side_fe_reinit();
268 
269  // If we find a boundary flux for any variable,
270  // let's just count it as a flux face for all
271  // variables. Otherwise we'd need to keep track of
272  // a separate n_flux_faces and error_per_cell for
273  // every single var.
274  bool found_boundary_flux = false;
275 
276  for (var=0; var<n_vars; var++)
277  if (error_norm.weight(var) != 0.0 &&
278  system.variable_type(var).family != SCALAR)
279  {
280  if (this->boundary_side_integration())
281  {
282  error_per_cell[fine_context->get_elem().id()] +=
283  static_cast<ErrorVectorReal>(fine_error);
284  found_boundary_flux = true;
285  }
286  }
287 
288  if (scale_by_n_flux_faces && found_boundary_flux)
289  n_flux_faces[fine_context->get_elem().id()]++;
290  }
291  }
292  }
293 #endif // #ifdef LIBMESH_ENABLE_AMR
294 
295  // If we do any more flux integration, e will be the fine element
296  fine_context->pre_fe_reinit(system, e);
297 
298  // Loop over the neighbors of element e
299  for (auto n_e : e->side_index_range())
300  {
301  if ((e->neighbor_ptr(n_e) != nullptr) ||
303  {
304  fine_context->side = cast_int<unsigned char>(n_e);
305  fine_context->side_fe_reinit();
306  }
307 
308  if (e->neighbor_ptr(n_e) != nullptr) // e is not on the boundary
309  {
310  const Elem * f = e->neighbor_ptr(n_e);
311  const dof_id_type f_id = f->id();
312 
313  // Compute flux jumps if we are in case 1 or case 2.
314  if ((f->active() && (f->level() == e->level()) && (e_id < f_id))
315  || (f->level() < e->level()))
316  {
317  // f is now the coarse element
318  coarse_context->pre_fe_reinit(system, f);
319 
320  this->reinit_sides();
321 
322  // Loop over all significant variables in the system
323  for (var=0; var<n_vars; var++)
324  if (error_norm.weight(var) != 0.0 &&
325  system.variable_type(var).family != SCALAR)
326  {
328 
329  error_per_cell[fine_context->get_elem().id()] +=
330  static_cast<ErrorVectorReal>(fine_error);
331  error_per_cell[coarse_context->get_elem().id()] +=
332  static_cast<ErrorVectorReal>(coarse_error);
333  }
334 
335  // Keep track of the number of internal flux
336  // sides found on each element
338  {
339  n_flux_faces[fine_context->get_elem().id()]++;
340  n_flux_faces[coarse_context->get_elem().id()] +=
342  }
343  } // end if (case1 || case2)
344  } // if (e->neighbor(n_e) != nullptr)
345 
346  // Otherwise, e is on the boundary. If it happens to
347  // be on a Dirichlet boundary, we need not do anything.
348  // On the other hand, if e is on a Neumann (flux) boundary
349  // with grad(u).n = g, we need to compute the additional residual
350  // (h * \int |g - grad(u_h).n|^2 dS)^(1/2).
351  // We can only do this with some knowledge of the boundary
352  // conditions, i.e. the user must have attached an appropriate
353  // BC function.
354  else if (integrate_boundary_sides)
355  {
356  bool found_boundary_flux = false;
357 
358  for (var=0; var<n_vars; var++)
359  if (error_norm.weight(var) != 0.0 &&
360  system.variable_type(var).family != SCALAR)
361  if (this->boundary_side_integration())
362  {
363  error_per_cell[fine_context->get_elem().id()] +=
364  static_cast<ErrorVectorReal>(fine_error);
365  found_boundary_flux = true;
366  }
367 
368  if (scale_by_n_flux_faces && found_boundary_flux)
369  n_flux_faces[fine_context->get_elem().id()]++;
370  } // end if (e->neighbor_ptr(n_e) == nullptr)
371  } // end loop over neighbors
372  } // End loop over active local elements
373 
374 
375  // Each processor has now computed the error contributions
376  // for its local elements. We need to sum the vector
377  // and then take the square-root of each component. Note
378  // that we only need to sum if we are running on multiple
379  // processors, and we only need to take the square-root
380  // if the value is nonzero. There will in general be many
381  // zeros for the inactive elements.
382 
383  // First sum the vector of estimated error values
384  this->reduce_error(error_per_cell, system.comm());
385 
386  // Compute the square-root of each component.
387  for (auto i : index_range(error_per_cell))
388  if (error_per_cell[i] != 0.)
389  error_per_cell[i] = std::sqrt(error_per_cell[i]);
390 
391 
392  if (this->scale_by_n_flux_faces)
393  {
394  // Sum the vector of flux face counts
395  this->reduce_error(n_flux_faces, system.comm());
396 
397  // Sanity check: Make sure the number of flux faces is
398  // always an integer value
399 #ifdef DEBUG
400  for (const auto & val : n_flux_faces)
401  libmesh_assert_equal_to (val, static_cast<float>(static_cast<unsigned int>(val)));
402 #endif
403 
404  // Scale the error by the number of flux faces for each element
405  for (auto i : index_range(n_flux_faces))
406  {
407  if (n_flux_faces[i] == 0.0) // inactive or non-local element
408  continue;
409 
410  error_per_cell[i] /= static_cast<ErrorVectorReal>(n_flux_faces[i]);
411  }
412  }
413 
414  // If we used a non-standard solution before, now is the time to fix
415  // the current_local_solution
416  if (solution_vector && solution_vector != system.solution.get())
417  {
418  NumericVector<Number> * newsol =
419  const_cast<NumericVector<Number> *>(solution_vector);
420  System & sys = const_cast<System &>(system);
421  newsol->swap(*sys.solution);
422  sys.update();
423  }
424 }
425 
426 
427 
428 void
430 {
431  fine_context->side_fe_reinit();
432 
433  unsigned short dim = fine_context->get_elem().dim();
434  libmesh_assert_equal_to(dim, coarse_context->get_elem().dim());
435 
436  FEBase * fe_fine = nullptr;
437  fine_context->get_side_fe( 0, fe_fine, dim );
438 
439  // Get the physical locations of the fine element quadrature points
440  std::vector<Point> qface_point = fe_fine->get_xyz();
441 
442  // Find the master quadrature point locations on the coarse element
443  FEBase * fe_coarse = nullptr;
444  coarse_context->get_side_fe( 0, fe_coarse, dim );
445 
446  std::vector<Point> qp_coarse;
447 
448  FEMap::inverse_map (coarse_context->get_elem().dim(),
449  &coarse_context->get_elem(), qface_point,
450  qp_coarse);
451 
452  // The number of variables in the system
453  const unsigned int n_vars = fine_context->n_vars();
454 
455  // Calculate all coarse element shape functions at those locations
456  for (unsigned int v=0; v<n_vars; v++)
457  if (error_norm.weight(v) != 0.0 &&
458  fine_context->get_system().variable_type(v).family != SCALAR)
459  {
460  coarse_context->get_side_fe( v, fe_coarse, dim );
461  fe_coarse->reinit (&coarse_context->get_elem(), &qp_coarse);
462  }
463 }
464 
465 
466 
468 {
469  // Keep track of the number of internal flux sides found on each
470  // element
471  unsigned short dim = coarse_context->get_elem().dim();
472 
473  const unsigned int divisor =
474  1 << (dim-1)*(fine_context->get_elem().level() -
475  coarse_context->get_elem().level());
476 
477  // With a difference of n levels between fine and coarse elements,
478  // we compute a fractional flux face for the coarse element by adding:
479  // 1/2^n in 2D
480  // 1/4^n in 3D
481  // each time. This code will get hit 2^n times in 2D and 4^n
482  // times in 3D so that the final flux face count for the coarse
483  // element will be an integer value.
484 
485  return 1.0f / static_cast<float>(divisor);
486 }
487 
488 } // namespace libMesh
libMesh::JumpErrorEstimator::reinit_sides
void reinit_sides()
A utility function to reinit the finite element data on elements sharing a side.
Definition: jump_error_estimator.C:429
libMesh::System
Manages consistently variables, degrees of freedom, and coefficient vectors.
Definition: system.h:100
libMesh::JumpErrorEstimator::coarse_context
std::unique_ptr< FEMContext > coarse_context
Definition: jump_error_estimator.h:158
libMesh::dof_id_type
uint8_t dof_id_type
Definition: id_types.h:67
libMesh::JumpErrorEstimator::integrate_boundary_sides
bool integrate_boundary_sides
A boolean flag, by default false, to be set to true if integrations with boundary_side_integration() ...
Definition: jump_error_estimator.h:152
libMesh::System::n_vars
unsigned int n_vars() const
Definition: system.h:2155
libMesh::FEAbstract::get_xyz
const std::vector< Point > & get_xyz() const
Definition: fe_abstract.h:237
libMesh::JumpErrorEstimator::init_context
virtual void init_context(FEMContext &c)
An initialization function, to give derived classes a chance to request specific data from the FE obj...
Definition: jump_error_estimator.C:47
libMesh::FEType::family
FEFamily family
The type of finite element.
Definition: fe_type.h:203
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::Elem::level
unsigned int level() const
Definition: elem.h:2478
libMesh::Elem::child_ref_range
SimpleRange< ChildRefIter > child_ref_range()
Returns a range with all children of a parent element, usable in range-based for loops.
Definition: elem.h:1839
libMesh::JumpErrorEstimator::estimate_error
virtual void estimate_error(const System &system, ErrorVector &error_per_cell, const NumericVector< Number > *solution_vector=nullptr, bool estimate_parent_error=false) override
This function uses the derived class's jump error estimate formula to estimate the error on each cell...
Definition: jump_error_estimator.C:53
libMesh::NumericVector::swap
virtual void swap(NumericVector< T > &v)
Swaps the contents of this with v.
Definition: numeric_vector.h:989
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
The libMesh namespace provides an interface to certain functionality in the library.
Definition: factoryfunction.C:55
libMesh::JumpErrorEstimator::var
unsigned int var
The variable number currently being evaluated.
Definition: jump_error_estimator.h:168
libMesh::FEGenericBase
This class forms the foundation from which generic finite elements may be derived.
Definition: exact_error_estimator.h:39
libMesh::System::extra_quadrature_order
int extra_quadrature_order
A member int that can be employed to indicate increased or reduced quadrature order.
Definition: system.h:1524
libMesh::JumpErrorEstimator::fine_error
Real fine_error
The fine and coarse error values to be set by each side_integration();.
Definition: jump_error_estimator.h:163
std::sqrt
MetaPhysicL::DualNumber< T, D > sqrt(const MetaPhysicL::DualNumber< T, D > &in)
libMesh::ParallelObject::comm
const Parallel::Communicator & comm() const
Definition: parallel_object.h:94
mesh
MeshBase & mesh
Definition: mesh_communication.C:1257
libMesh::SystemNorm::weight
Real weight(unsigned int var) const
Definition: system_norm.C:133
libMesh::JumpErrorEstimator::scale_by_n_flux_faces
bool scale_by_n_flux_faces
This boolean flag allows you to scale the error indicator result for each element by the number of "f...
Definition: jump_error_estimator.h:99
libMesh::JumpErrorEstimator::coarse_n_flux_faces_increment
float coarse_n_flux_faces_increment()
A utility function to correctly increase n_flux_faces for the coarse element.
Definition: jump_error_estimator.C:467
libMesh::Elem::active
bool active() const
Definition: elem.h:2345
dim
unsigned int dim
Definition: adaptivity_ex3.C:113
libMesh::NumericVector< Number >
libMesh::JumpErrorEstimator::boundary_side_integration
virtual bool boundary_side_integration()
The function, to be implemented by derived classes, which calculates an error term on a boundary side...
Definition: jump_error_estimator.h:146
libMesh::JumpErrorEstimator::coarse_error
Real coarse_error
Definition: jump_error_estimator.h:163
libMesh::MeshBase
This is the MeshBase class.
Definition: mesh_base.h:78
libMesh::FEMap::inverse_map
static Point inverse_map(const unsigned int dim, const Elem *elem, const Point &p, const Real tolerance=TOLERANCE, const bool secure=true)
Definition: fe_map.C:1622
libMesh::JumpErrorEstimator::fine_context
std::unique_ptr< FEMContext > fine_context
Context objects for integrating on the fine and coarse elements sharing a face.
Definition: jump_error_estimator.h:158
libMesh::System::get_mesh
const MeshBase & get_mesh() const
Definition: system.h:2083
libMesh::libmesh_ignore
void libmesh_ignore(const Args &...)
Definition: libmesh_common.h:526
libMesh::ErrorVector
The ErrorVector is a specialization of the StatisticsVector for error data computed on a finite eleme...
Definition: error_vector.h:50
libMesh::DenseVector::size
virtual unsigned int size() const override
Definition: dense_vector.h:92
libMesh::FEGenericBase::coarsened_dof_values
static void coarsened_dof_values(const NumericVector< Number > &global_vector, const DofMap &dof_map, const Elem *coarse_elem, DenseVector< Number > &coarse_dofs, const unsigned int var, const bool use_old_dof_indices=false)
Creates a local projection on coarse_elem, based on the DoF values in global_vector for it's children...
Definition: fe_base.C:822
libMesh::ElemInternal::active_family_tree_by_neighbor
void active_family_tree_by_neighbor(T elem, std::vector< T > &family, T neighbor_in, bool reset=true)
Definition: elem_internal.h:320
libMesh::System::variable_type
const FEType & variable_type(const unsigned int i) const
Definition: system.h:2233
libMesh::Elem::parent
const Elem * parent() const
Definition: elem.h:2434
libMesh::JumpErrorEstimator::internal_side_integration
virtual void internal_side_integration()=0
The function, to be implemented by derived classes, which calculates an error term on an internal sid...
libMesh::System::solution
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
Definition: system.h:1539
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::DofMap
This class handles the numbering of degrees of freedom on a mesh.
Definition: dof_map.h:176
libMesh::DofObject::id
dof_id_type id() const
Definition: dof_object.h:767
libMesh::Elem::side_index_range
IntRange< unsigned short > side_index_range() const
Definition: elem.h:2188
libMesh::Elem
This is the base class from which all geometric element types are derived.
Definition: elem.h:100
libMesh::JumpErrorEstimator::use_unweighted_quadrature_rules
bool use_unweighted_quadrature_rules
This boolean flag allows you to use "unweighted" quadrature rules (sized to exactly integrate unweigh...
Definition: jump_error_estimator.h:113
libMesh::System::get_dof_map
const DofMap & get_dof_map() const
Definition: system.h:2099
libMesh::SCALAR
Definition: enum_fe_family.h:58
libMesh::Elem::neighbor_ptr
const Elem * neighbor_ptr(unsigned int i) const
Definition: elem.h:2085
libMesh::FEAbstract::reinit
virtual void reinit(const Elem *elem, const std::vector< Point > *const pts=nullptr, const std::vector< Real > *const weights=nullptr)=0
This is at the core of this class.
libMesh::DenseVector< Number >
libMesh::FEMContext
This class provides all data required for a physics package (e.g.
Definition: fem_context.h:62