void JumpErrorEstimator::estimate_error (const System& system, ErrorVector& error_per_cell, const NumericVector<Number>* solution_vector, bool estimate_parent_error) { START_LOG("estimate_error()", "JumpErrorEstimator"); /* Conventions for assigning the direction of the normal: - e & f are global element ids Case (1.) Elements are at the same level, e<f Compute the flux jump on the face and add it as a contribution to error_per_cell[e] and error_per_cell[f] ---------------------- | | | | | f | | | | | e |---> n | | | | | | | ---------------------- Case (2.) The neighbor is at a higher level. Compute the flux jump on e's face and add it as a contribution to error_per_cell[e] and error_per_cell[f] ---------------------- | | | | | | e |---> n | | | | | |-----------| f | | | | | | | | | | | | | ---------------------- */ // The current mesh const MeshBase& mesh = system.get_mesh(); // The number of variables in the system const unsigned int n_vars = system.n_vars(); // The DofMap for this system const DofMap& dof_map = system.get_dof_map(); // Resize the error_per_cell vector to be // the number of elements, initialize it to 0. error_per_cell.resize (mesh.max_elem_id()); std::fill (error_per_cell.begin(), error_per_cell.end(), 0.); // Declare a vector of floats which is as long as // error_per_cell above, and fill with zeros. This vector will be // used to keep track of the number of edges (faces) on each active // element which are either: // 1) an internal edge // 2) an edge on a Neumann boundary for which a boundary condition // function has been specified. // The error estimator can be scaled by the number of flux edges (faces) // which the element actually has to obtain a more uniform measure // of the error. Use floats instead of ints since in case 2 (above) // f gets 1/2 of a flux face contribution from each of his // neighbors std::vector<float> n_flux_faces; if (scale_by_n_flux_faces) n_flux_faces.resize(error_per_cell.size(), 0); // Prepare current_local_solution to localize a non-standard // solution vector if necessary if (solution_vector && solution_vector != system.solution.get()) { NumericVector<Number>* newsol = const_cast<NumericVector<Number>*>(solution_vector); System &sys = const_cast<System&>(system); newsol->swap(*sys.solution); sys.update(); } fine_context.reset(new FEMContext(system)); coarse_context.reset(new FEMContext(system)); // Loop over all the variables we've been requested to find jumps in, to // pre-request for (var=0; var<n_vars; var++) { // Possibly skip this variable if (error_norm.weight(var) == 0.0) continue; // FIXME: Need to generalize this to vector-valued elements. [PB] FEBase* side_fe = NULL; const std::set<unsigned char>& elem_dims = fine_context->elem_dimensions(); for (std::set<unsigned char>::const_iterator dim_it = elem_dims.begin(); dim_it != elem_dims.end(); ++dim_it) { const unsigned char dim = *dim_it; fine_context->get_side_fe( var, side_fe, dim ); libmesh_assert_not_equal_to(side_fe->get_fe_type().family, SCALAR); side_fe->get_xyz(); } } this->init_context(*fine_context); this->init_context(*coarse_context); // Iterate over all the active elements in the mesh // that live on this processor. MeshBase::const_element_iterator elem_it = mesh.active_local_elements_begin(); const MeshBase::const_element_iterator elem_end = mesh.active_local_elements_end(); for (; elem_it != elem_end; ++elem_it) { // e is necessarily an active element on the local processor const Elem* e = *elem_it; const dof_id_type e_id = e->id(); #ifdef LIBMESH_ENABLE_AMR // See if the parent of element e has been examined yet; // if not, we may want to compute the estimator on it const Elem* parent = e->parent(); // We only can compute and only need to compute on // parents with all active children bool compute_on_parent = true; if (!parent || !estimate_parent_error) compute_on_parent = false; else for (unsigned int c=0; c != parent->n_children(); ++c) if (!parent->child(c)->active()) compute_on_parent = false; if (compute_on_parent && !error_per_cell[parent->id()]) { // Compute a projection onto the parent DenseVector<Number> Uparent; FEBase::coarsened_dof_values (*(system.solution), dof_map, parent, Uparent, false); // Loop over the neighbors of the parent for (unsigned int n_p=0; n_p<parent->n_neighbors(); n_p++) { if (parent->neighbor(n_p) != NULL) // parent has a neighbor here { // Find the active neighbors in this direction std::vector<const Elem*> active_neighbors; parent->neighbor(n_p)-> active_family_tree_by_neighbor(active_neighbors, parent); // Compute the flux to each active neighbor for (unsigned int a=0; a != active_neighbors.size(); ++a) { const Elem *f = active_neighbors[a]; // FIXME - what about when f->level < // parent->level()?? if (f->level() >= parent->level()) { fine_context->pre_fe_reinit(system, f); coarse_context->pre_fe_reinit(system, parent); libmesh_assert_equal_to (coarse_context->get_elem_solution().size(), Uparent.size()); coarse_context->get_elem_solution() = Uparent; this->reinit_sides(); // Loop over all significant variables in the system for (var=0; var<n_vars; var++) if (error_norm.weight(var) != 0.0) { this->internal_side_integration(); error_per_cell[fine_context->get_elem().id()] += static_cast<ErrorVectorReal>(fine_error); error_per_cell[coarse_context->get_elem().id()] += static_cast<ErrorVectorReal>(coarse_error); } // Keep track of the number of internal flux // sides found on each element if (scale_by_n_flux_faces) { n_flux_faces[fine_context->get_elem().id()]++; n_flux_faces[coarse_context->get_elem().id()] += this->coarse_n_flux_faces_increment(); } } } } else if (integrate_boundary_sides) { fine_context->pre_fe_reinit(system, parent); libmesh_assert_equal_to (fine_context->get_elem_solution().size(), Uparent.size()); fine_context->get_elem_solution() = Uparent; fine_context->side = n_p; fine_context->side_fe_reinit(); // If we find a boundary flux for any variable, // let's just count it as a flux face for all // variables. Otherwise we'd need to keep track of // a separate n_flux_faces and error_per_cell for // every single var. bool found_boundary_flux = false; for (var=0; var<n_vars; var++) if (error_norm.weight(var) != 0.0) { if (this->boundary_side_integration()) { error_per_cell[fine_context->get_elem().id()] += static_cast<ErrorVectorReal>(fine_error); found_boundary_flux = true; } } if (scale_by_n_flux_faces && found_boundary_flux) n_flux_faces[fine_context->get_elem().id()]++; } } } #endif // #ifdef LIBMESH_ENABLE_AMR // If we do any more flux integration, e will be the fine element fine_context->pre_fe_reinit(system, e); // Loop over the neighbors of element e for (unsigned int n_e=0; n_e<e->n_neighbors(); n_e++) { if ((e->neighbor(n_e) != NULL) || integrate_boundary_sides) { fine_context->side = n_e; fine_context->side_fe_reinit(); } if (e->neighbor(n_e) != NULL) // e is not on the boundary { const Elem* f = e->neighbor(n_e); const dof_id_type f_id = f->id(); // Compute flux jumps if we are in case 1 or case 2. if ((f->active() && (f->level() == e->level()) && (e_id < f_id)) || (f->level() < e->level())) { // f is now the coarse element coarse_context->pre_fe_reinit(system, f); this->reinit_sides(); // Loop over all significant variables in the system for (var=0; var<n_vars; var++) if (error_norm.weight(var) != 0.0) { this->internal_side_integration(); error_per_cell[fine_context->get_elem().id()] += static_cast<ErrorVectorReal>(fine_error); error_per_cell[coarse_context->get_elem().id()] += static_cast<ErrorVectorReal>(coarse_error); } // Keep track of the number of internal flux // sides found on each element if (scale_by_n_flux_faces) { n_flux_faces[fine_context->get_elem().id()]++; n_flux_faces[coarse_context->get_elem().id()] += this->coarse_n_flux_faces_increment(); } } // end if (case1 || case2) } // if (e->neigbor(n_e) != NULL) // Otherwise, e is on the boundary. If it happens to // be on a Dirichlet boundary, we need not do anything. // On the other hand, if e is on a Neumann (flux) boundary // with grad(u).n = g, we need to compute the additional residual // (h * \int |g - grad(u_h).n|^2 dS)^(1/2). // We can only do this with some knowledge of the boundary // conditions, i.e. the user must have attached an appropriate // BC function. else if (integrate_boundary_sides) { bool found_boundary_flux = false; for (var=0; var<n_vars; var++) if (error_norm.weight(var) != 0.0) if (this->boundary_side_integration()) { error_per_cell[fine_context->get_elem().id()] += static_cast<ErrorVectorReal>(fine_error); found_boundary_flux = true; } if (scale_by_n_flux_faces && found_boundary_flux) n_flux_faces[fine_context->get_elem().id()]++; } // end if (e->neighbor(n_e) == NULL) } // end loop over neighbors } // End loop over active local elements // Each processor has now computed the error contribuions // for its local elements. We need to sum the vector // and then take the square-root of each component. Note // that we only need to sum if we are running on multiple // processors, and we only need to take the square-root // if the value is nonzero. There will in general be many // zeros for the inactive elements. // First sum the vector of estimated error values this->reduce_error(error_per_cell, system.comm()); // Compute the square-root of each component. for (std::size_t i=0; i<error_per_cell.size(); i++) if (error_per_cell[i] != 0.) error_per_cell[i] = std::sqrt(error_per_cell[i]); if (this->scale_by_n_flux_faces) { // Sum the vector of flux face counts this->reduce_error(n_flux_faces, system.comm()); // Sanity check: Make sure the number of flux faces is // always an integer value #ifdef DEBUG for (unsigned int i=0; i<n_flux_faces.size(); ++i) libmesh_assert_equal_to (n_flux_faces[i], static_cast<float>(static_cast<unsigned int>(n_flux_faces[i])) ); #endif // Scale the error by the number of flux faces for each element for (unsigned int i=0; i<n_flux_faces.size(); ++i) { if (n_flux_faces[i] == 0.0) // inactive or non-local element continue; //libMesh::out << "Element " << i << " has " << n_flux_faces[i] << " flux faces." << std::endl; error_per_cell[i] /= static_cast<ErrorVectorReal>(n_flux_faces[i]); } } // If we used a non-standard solution before, now is the time to fix // the current_local_solution if (solution_vector && solution_vector != system.solution.get()) { NumericVector<Number>* newsol = const_cast<NumericVector<Number>*>(solution_vector); System &sys = const_cast<System&>(system); newsol->swap(*sys.solution); sys.update(); } STOP_LOG("estimate_error()", "JumpErrorEstimator"); }
void ExactErrorEstimator::estimate_error (const System & system, ErrorVector & error_per_cell, const NumericVector<Number> * solution_vector, bool estimate_parent_error) { // Ignore the fact that this variable is unused when !LIBMESH_ENABLE_AMR libmesh_ignore(estimate_parent_error); // The current mesh const MeshBase & mesh = system.get_mesh(); // The dimensionality of the mesh const unsigned int dim = mesh.mesh_dimension(); // The number of variables in the system const unsigned int n_vars = system.n_vars(); // The DofMap for this system const DofMap & dof_map = system.get_dof_map(); // Resize the error_per_cell vector to be // the number of elements, initialize it to 0. error_per_cell.resize (mesh.max_elem_id()); std::fill (error_per_cell.begin(), error_per_cell.end(), 0.); // Prepare current_local_solution to localize a non-standard // solution vector if necessary if (solution_vector && solution_vector != system.solution.get()) { NumericVector<Number> * newsol = const_cast<NumericVector<Number> *>(solution_vector); System & sys = const_cast<System &>(system); newsol->swap(*sys.solution); sys.update(); } // Loop over all the variables in the system for (unsigned int var=0; var<n_vars; var++) { // Possibly skip this variable if (error_norm.weight(var) == 0.0) continue; // The (string) name of this variable const std::string & var_name = system.variable_name(var); // The type of finite element to use for this variable const FEType & fe_type = dof_map.variable_type (var); UniquePtr<FEBase> fe (FEBase::build (dim, fe_type)); // Build an appropriate Gaussian quadrature rule UniquePtr<QBase> qrule = fe_type.default_quadrature_rule (dim, _extra_order); fe->attach_quadrature_rule (qrule.get()); // Prepare a global solution and a MeshFunction of the fine system if we need one UniquePtr<MeshFunction> fine_values; UniquePtr<NumericVector<Number> > fine_soln = NumericVector<Number>::build(system.comm()); if (_equation_systems_fine) { const System & fine_system = _equation_systems_fine->get_system(system.name()); std::vector<Number> global_soln; // FIXME - we're assuming that the fine system solution gets // used even when a different vector is used for the coarse // system fine_system.update_global_solution(global_soln); fine_soln->init (cast_int<numeric_index_type>(global_soln.size()), true, SERIAL); (*fine_soln) = global_soln; fine_values = UniquePtr<MeshFunction> (new MeshFunction(*_equation_systems_fine, *fine_soln, fine_system.get_dof_map(), fine_system.variable_number(var_name))); fine_values->init(); } else { // Initialize functors if we're using them for (unsigned int i=0; i != _exact_values.size(); ++i) if (_exact_values[i]) _exact_values[i]->init(); for (unsigned int i=0; i != _exact_derivs.size(); ++i) if (_exact_derivs[i]) _exact_derivs[i]->init(); for (unsigned int i=0; i != _exact_hessians.size(); ++i) if (_exact_hessians[i]) _exact_hessians[i]->init(); } // Request the data we'll need to compute with fe->get_JxW(); fe->get_phi(); fe->get_dphi(); #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES fe->get_d2phi(); #endif fe->get_xyz(); #ifdef LIBMESH_ENABLE_AMR // If we compute on parent elements, we'll want to do so only // once on each, so we need to keep track of which we've done. std::vector<bool> computed_var_on_parent; if (estimate_parent_error) computed_var_on_parent.resize(error_per_cell.size(), false); #endif // TODO: this ought to be threaded (and using subordinate // MeshFunction objects in each thread rather than a single // master) // Iterate over all the active elements in the mesh // that live on this processor. MeshBase::const_element_iterator elem_it = mesh.active_local_elements_begin(); const MeshBase::const_element_iterator elem_end = mesh.active_local_elements_end(); for (;elem_it != elem_end; ++elem_it) { // e is necessarily an active element on the local processor const Elem * elem = *elem_it; const dof_id_type e_id = elem->id(); #ifdef LIBMESH_ENABLE_AMR // See if the parent of element e has been examined yet; // if not, we may want to compute the estimator on it const Elem * parent = elem->parent(); // We only can compute and only need to compute on // parents with all active children bool compute_on_parent = true; if (!parent || !estimate_parent_error) compute_on_parent = false; else for (unsigned int c=0; c != parent->n_children(); ++c) if (!parent->child_ptr(c)->active()) compute_on_parent = false; if (compute_on_parent && !computed_var_on_parent[parent->id()]) { computed_var_on_parent[parent->id()] = true; // Compute a projection onto the parent DenseVector<Number> Uparent; FEBase::coarsened_dof_values(*(system.current_local_solution), dof_map, parent, Uparent, var, false); error_per_cell[parent->id()] += static_cast<ErrorVectorReal> (find_squared_element_error(system, var_name, parent, Uparent, fe.get(), fine_values.get())); } #endif // Get the local to global degree of freedom maps std::vector<dof_id_type> dof_indices; dof_map.dof_indices (elem, dof_indices, var); const unsigned int n_dofs = cast_int<unsigned int>(dof_indices.size()); DenseVector<Number> Uelem(n_dofs); for (unsigned int i=0; i != n_dofs; ++i) Uelem(i) = system.current_solution(dof_indices[i]); error_per_cell[e_id] += static_cast<ErrorVectorReal> (find_squared_element_error(system, var_name, elem, Uelem, fe.get(), fine_values.get())); } // End loop over active local elements } // End loop over variables // Each processor has now computed the error contribuions // for its local elements. We need to sum the vector // and then take the square-root of each component. Note // that we only need to sum if we are running on multiple // processors, and we only need to take the square-root // if the value is nonzero. There will in general be many // zeros for the inactive elements. // First sum the vector of estimated error values this->reduce_error(error_per_cell, system.comm()); // Compute the square-root of each component. { LOG_SCOPE("std::sqrt()", "ExactErrorEstimator"); for (dof_id_type i=0; i<error_per_cell.size(); i++) if (error_per_cell[i] != 0.) { libmesh_assert_greater (error_per_cell[i], 0.); error_per_cell[i] = std::sqrt(error_per_cell[i]); } } // If we used a non-standard solution before, now is the time to fix // the current_local_solution if (solution_vector && solution_vector != system.solution.get()) { NumericVector<Number> * newsol = const_cast<NumericVector<Number> *>(solution_vector); System & sys = const_cast<System &>(system); newsol->swap(*sys.solution); sys.update(); } }
void JumpErrorEstimator::estimate_error (const System& system, ErrorVector& error_per_cell, const NumericVector<Number>* solution_vector, bool estimate_parent_error) { START_LOG("estimate_error()", "JumpErrorEstimator"); /* Conventions for assigning the direction of the normal: - e & f are global element ids Case (1.) Elements are at the same level, e<f Compute the flux jump on the face and add it as a contribution to error_per_cell[e] and error_per_cell[f] ---------------------- | | | | | f | | | | | e |---> n | | | | | | | ---------------------- Case (2.) The neighbor is at a higher level. Compute the flux jump on e's face and add it as a contribution to error_per_cell[e] and error_per_cell[f] ---------------------- | | | | | | e |---> n | | | | | |-----------| f | | | | | | | | | | | | | ---------------------- */ // The current mesh const MeshBase& mesh = system.get_mesh(); // The dimensionality of the mesh const unsigned int dim = mesh.mesh_dimension(); // The number of variables in the system const unsigned int n_vars = system.n_vars(); // The DofMap for this system const DofMap& dof_map = system.get_dof_map(); // Resize the error_per_cell vector to be // the number of elements, initialize it to 0. error_per_cell.resize (mesh.max_elem_id()); std::fill (error_per_cell.begin(), error_per_cell.end(), 0.); // Declare a vector of floats which is as long as // error_per_cell above, and fill with zeros. This vector will be // used to keep track of the number of edges (faces) on each active // element which are either: // 1) an internal edge // 2) an edge on a Neumann boundary for which a boundary condition // function has been specified. // The error estimator can be scaled by the number of flux edges (faces) // which the element actually has to obtain a more uniform measure // of the error. Use floats instead of ints since in case 2 (above) // f gets 1/2 of a flux face contribution from each of his // neighbors std::vector<float> n_flux_faces (error_per_cell.size()); // Prepare current_local_solution to localize a non-standard // solution vector if necessary if (solution_vector && solution_vector != system.solution.get()) { NumericVector<Number>* newsol = const_cast<NumericVector<Number>*>(solution_vector); System &sys = const_cast<System&>(system); newsol->swap(*sys.solution); sys.update(); } // Loop over all the variables in the system for (var=0; var<n_vars; var++) { // Possibly skip this variable if (error_norm.weight(var) == 0.0) continue; // The type of finite element to use for this variable const FEType& fe_type = dof_map.variable_type (var); // Finite element objects for the same face from // different sides fe_fine = FEBase::build (dim, fe_type); fe_coarse = FEBase::build (dim, fe_type); // Build an appropriate Gaussian quadrature rule QGauss qrule (dim-1, fe_type.default_quadrature_order()); // Tell the finite element for the fine element about the quadrature // rule. The finite element for the coarse element need not know about it fe_fine->attach_quadrature_rule (&qrule); // By convention we will always do the integration // on the face of element e. We'll need its Jacobian values and // physical point locations, at least fe_fine->get_JxW(); fe_fine->get_xyz(); // Our derived classes may want to do some initialization here this->initialize(system, error_per_cell, estimate_parent_error); // The global DOF indices for elements e & f std::vector<dof_id_type> dof_indices_fine; std::vector<dof_id_type> dof_indices_coarse; // Iterate over all the active elements in the mesh // that live on this processor. MeshBase::const_element_iterator elem_it = mesh.active_local_elements_begin(); const MeshBase::const_element_iterator elem_end = mesh.active_local_elements_end(); for (; elem_it != elem_end; ++elem_it) { // e is necessarily an active element on the local processor const Elem* e = *elem_it; const dof_id_type e_id = e->id(); #ifdef LIBMESH_ENABLE_AMR // See if the parent of element e has been examined yet; // if not, we may want to compute the estimator on it const Elem* parent = e->parent(); // We only can compute and only need to compute on // parents with all active children bool compute_on_parent = true; if (!parent || !estimate_parent_error) compute_on_parent = false; else for (unsigned int c=0; c != parent->n_children(); ++c) if (!parent->child(c)->active()) compute_on_parent = false; if (compute_on_parent && !error_per_cell[parent->id()]) { // Compute a projection onto the parent DenseVector<Number> Uparent; FEBase::coarsened_dof_values(*(system.solution), dof_map, parent, Uparent, var, false); // Loop over the neighbors of the parent for (unsigned int n_p=0; n_p<parent->n_neighbors(); n_p++) { if (parent->neighbor(n_p) != NULL) // parent has a neighbor here { // Find the active neighbors in this direction std::vector<const Elem*> active_neighbors; parent->neighbor(n_p)-> active_family_tree_by_neighbor(active_neighbors, parent); // Compute the flux to each active neighbor for (unsigned int a=0; a != active_neighbors.size(); ++a) { const Elem *f = active_neighbors[a]; // FIXME - what about when f->level < // parent->level()?? if (f->level() >= parent->level()) { fine_elem = f; coarse_elem = parent; Ucoarse = Uparent; dof_map.dof_indices (fine_elem, dof_indices_fine, var); const unsigned int n_dofs_fine = libmesh_cast_int<unsigned int>(dof_indices_fine.size()); Ufine.resize(n_dofs_fine); for (unsigned int i=0; i<n_dofs_fine; i++) Ufine(i) = system.current_solution(dof_indices_fine[i]); this->reinit_sides(); this->internal_side_integration(); error_per_cell[fine_elem->id()] += static_cast<ErrorVectorReal>(fine_error); error_per_cell[coarse_elem->id()] += static_cast<ErrorVectorReal>(coarse_error); // Keep track of the number of internal flux // sides found on each element n_flux_faces[fine_elem->id()]++; n_flux_faces[coarse_elem->id()] += this->coarse_n_flux_faces_increment(); } } } else if (integrate_boundary_sides) { fine_elem = parent; Ufine = Uparent; // Reinitialize shape functions on the fine element side fe_fine->reinit (fine_elem, fine_side); if (this->boundary_side_integration()) { error_per_cell[fine_elem->id()] += static_cast<ErrorVectorReal>(fine_error); n_flux_faces[fine_elem->id()]++; } } } } #endif // #ifdef LIBMESH_ENABLE_AMR // If we do any more flux integration, e will be the fine element fine_elem = e; // Loop over the neighbors of element e for (unsigned int n_e=0; n_e<e->n_neighbors(); n_e++) { fine_side = n_e; if (e->neighbor(n_e) != NULL) // e is not on the boundary { const Elem* f = e->neighbor(n_e); const dof_id_type f_id = f->id(); // Compute flux jumps if we are in case 1 or case 2. if ((f->active() && (f->level() == e->level()) && (e_id < f_id)) || (f->level() < e->level())) { // f is now the coarse element coarse_elem = f; // Get the DOF indices for the two elements dof_map.dof_indices (fine_elem, dof_indices_fine, var); dof_map.dof_indices (coarse_elem, dof_indices_coarse, var); // The number of DOFS on each element const unsigned int n_dofs_fine = libmesh_cast_int<unsigned int>(dof_indices_fine.size()); const unsigned int n_dofs_coarse = libmesh_cast_int<unsigned int>(dof_indices_coarse.size()); Ufine.resize(n_dofs_fine); Ucoarse.resize(n_dofs_coarse); // The local solutions on each element for (unsigned int i=0; i<n_dofs_fine; i++) Ufine(i) = system.current_solution(dof_indices_fine[i]); for (unsigned int i=0; i<n_dofs_coarse; i++) Ucoarse(i) = system.current_solution(dof_indices_coarse[i]); this->reinit_sides(); this->internal_side_integration(); error_per_cell[fine_elem->id()] += static_cast<ErrorVectorReal>(fine_error); error_per_cell[coarse_elem->id()] += static_cast<ErrorVectorReal>(coarse_error); // Keep track of the number of internal flux // sides found on each element n_flux_faces[fine_elem->id()]++; n_flux_faces[coarse_elem->id()] += this->coarse_n_flux_faces_increment(); } // end if (case1 || case2) } // if (e->neigbor(n_e) != NULL) // Otherwise, e is on the boundary. If it happens to // be on a Dirichlet boundary, we need not do anything. // On the other hand, if e is on a Neumann (flux) boundary // with grad(u).n = g, we need to compute the additional residual // (h * \int |g - grad(u_h).n|^2 dS)^(1/2). // We can only do this with some knowledge of the boundary // conditions, i.e. the user must have attached an appropriate // BC function. else { if (integrate_boundary_sides) { // Reinitialize shape functions on the fine element side fe_fine->reinit (fine_elem, fine_side); // Get the DOF indices dof_map.dof_indices (fine_elem, dof_indices_fine, var); // The number of DOFS on each element const unsigned int n_dofs_fine = libmesh_cast_int<unsigned int>(dof_indices_fine.size()); Ufine.resize(n_dofs_fine); for (unsigned int i=0; i<n_dofs_fine; i++) Ufine(i) = system.current_solution(dof_indices_fine[i]); if (this->boundary_side_integration()) { error_per_cell[fine_elem->id()] += static_cast<ErrorVectorReal>(fine_error); n_flux_faces[fine_elem->id()]++; } } // end if _bc_function != NULL } // end if (e->neighbor(n_e) == NULL) } // end loop over neighbors } // End loop over active local elements } // End loop over variables // Each processor has now computed the error contribuions // for its local elements. We need to sum the vector // and then take the square-root of each component. Note // that we only need to sum if we are running on multiple // processors, and we only need to take the square-root // if the value is nonzero. There will in general be many // zeros for the inactive elements. // First sum the vector of estimated error values this->reduce_error(error_per_cell); // Compute the square-root of each component. for (std::size_t i=0; i<error_per_cell.size(); i++) if (error_per_cell[i] != 0.) error_per_cell[i] = std::sqrt(error_per_cell[i]); if (this->scale_by_n_flux_faces) { // Sum the vector of flux face counts this->reduce_error(n_flux_faces); // Sanity check: Make sure the number of flux faces is // always an integer value #ifdef DEBUG for (unsigned int i=0; i<n_flux_faces.size(); ++i) libmesh_assert_equal_to (n_flux_faces[i], static_cast<float>(static_cast<unsigned int>(n_flux_faces[i])) ); #endif // Scale the error by the number of flux faces for each element for (unsigned int i=0; i<n_flux_faces.size(); ++i) { if (n_flux_faces[i] == 0.0) // inactive or non-local element continue; //libMesh::out << "Element " << i << " has " << n_flux_faces[i] << " flux faces." << std::endl; error_per_cell[i] /= static_cast<ErrorVectorReal>(n_flux_faces[i]); } } // If we used a non-standard solution before, now is the time to fix // the current_local_solution if (solution_vector && solution_vector != system.solution.get()) { NumericVector<Number>* newsol = const_cast<NumericVector<Number>*>(solution_vector); System &sys = const_cast<System&>(system); newsol->swap(*sys.solution); sys.update(); } STOP_LOG("estimate_error()", "JumpErrorEstimator"); }
void AdjointResidualErrorEstimator::estimate_error (const System & _system, ErrorVector & error_per_cell, const NumericVector<Number> * solution_vector, bool estimate_parent_error) { LOG_SCOPE("estimate_error()", "AdjointResidualErrorEstimator"); // The current mesh const MeshBase & mesh = _system.get_mesh(); // Resize the error_per_cell vector to be // the number of elements, initialize it to 0. error_per_cell.resize (mesh.max_elem_id()); std::fill (error_per_cell.begin(), error_per_cell.end(), 0.); // Get the number of variables in the system unsigned int n_vars = _system.n_vars(); // We need to make a map of the pointer to the solution vector std::map<const System *, const NumericVector<Number> *>solutionvecs; solutionvecs[&_system] = _system.solution.get(); // Solve the dual problem if we have to if (!_system.is_adjoint_already_solved()) { // FIXME - we'll need to change a lot of APIs to make this trick // work with a const System... System & system = const_cast<System &>(_system); system.adjoint_solve(_qoi_set); } // Flag to check whether we have not been asked to weight the variable error contributions in any specific manner bool error_norm_is_identity = error_norm.is_identity(); // Create an ErrorMap/ErrorVector to store the primal, dual and total_dual variable errors ErrorMap primal_errors_per_cell; ErrorMap dual_errors_per_cell; ErrorMap total_dual_errors_per_cell; // Allocate ErrorVectors to this map if we're going to use it if (!error_norm_is_identity) for(unsigned int v = 0; v < n_vars; v++) { primal_errors_per_cell[std::make_pair(&_system, v)] = new ErrorVector; dual_errors_per_cell[std::make_pair(&_system, v)] = new ErrorVector; total_dual_errors_per_cell[std::make_pair(&_system, v)] = new ErrorVector; } ErrorVector primal_error_per_cell; ErrorVector dual_error_per_cell; ErrorVector total_dual_error_per_cell; // Have we been asked to weight the variable error contributions in any specific manner if(!error_norm_is_identity) // If we do { // Estimate the primal problem error for each variable _primal_error_estimator->estimate_errors (_system.get_equation_systems(), primal_errors_per_cell, &solutionvecs, estimate_parent_error); } else // If not { // Just get the combined error estimate _primal_error_estimator->estimate_error (_system, primal_error_per_cell, solution_vector, estimate_parent_error); } // Sum and weight the dual error estimate based on our QoISet for (unsigned int i = 0; i != _system.qoi.size(); ++i) { if (_qoi_set.has_index(i)) { // Get the weight for the current QoI Real error_weight = _qoi_set.weight(i); // We need to make a map of the pointer to the adjoint solution vector std::map<const System *, const NumericVector<Number> *>adjointsolutionvecs; adjointsolutionvecs[&_system] = &_system.get_adjoint_solution(i); // Have we been asked to weight the variable error contributions in any specific manner if(!error_norm_is_identity) // If we have { _dual_error_estimator->estimate_errors (_system.get_equation_systems(), dual_errors_per_cell, &adjointsolutionvecs, estimate_parent_error); } else // If not { // Just get the combined error estimate _dual_error_estimator->estimate_error (_system, dual_error_per_cell, &(_system.get_adjoint_solution(i)), estimate_parent_error); } std::size_t error_size; // Get the size of the first ErrorMap vector; this will give us the number of elements if(!error_norm_is_identity) // If in non default weights case { error_size = dual_errors_per_cell[std::make_pair(&_system, 0)]->size(); } else // If in the standard default weights case { error_size = dual_error_per_cell.size(); } // Resize the ErrorVector(s) if(!error_norm_is_identity) { // Loop over variables for(unsigned int v = 0; v < n_vars; v++) { libmesh_assert(!total_dual_errors_per_cell[std::make_pair(&_system, v)]->size() || total_dual_errors_per_cell[std::make_pair(&_system, v)]->size() == error_size) ; total_dual_errors_per_cell[std::make_pair(&_system, v)]->resize(error_size); } } else { libmesh_assert(!total_dual_error_per_cell.size() || total_dual_error_per_cell.size() == error_size); total_dual_error_per_cell.resize(error_size); } for (std::size_t e = 0; e != error_size; ++e) { // Have we been asked to weight the variable error contributions in any specific manner if(!error_norm_is_identity) // If we have { // Loop over variables for(unsigned int v = 0; v < n_vars; v++) { // Now fill in total_dual_error ErrorMap with the weight (*total_dual_errors_per_cell[std::make_pair(&_system, v)])[e] += static_cast<ErrorVectorReal> (error_weight * (*dual_errors_per_cell[std::make_pair(&_system, v)])[e]); } } else // If not { total_dual_error_per_cell[e] += static_cast<ErrorVectorReal>(error_weight * dual_error_per_cell[e]); } } } } // Do some debugging plots if requested if (!error_plot_suffix.empty()) { if(!error_norm_is_identity) // If we have { // Loop over variables for(unsigned int v = 0; v < n_vars; v++) { std::ostringstream primal_out; std::ostringstream dual_out; primal_out << "primal_" << error_plot_suffix << "."; dual_out << "dual_" << error_plot_suffix << "."; primal_out << std::setw(1) << std::setprecision(0) << std::setfill('0') << std::right << v; dual_out << std::setw(1) << std::setprecision(0) << std::setfill('0') << std::right << v; (*primal_errors_per_cell[std::make_pair(&_system, v)]).plot_error(primal_out.str(), _system.get_mesh()); (*total_dual_errors_per_cell[std::make_pair(&_system, v)]).plot_error(dual_out.str(), _system.get_mesh()); primal_out.clear(); dual_out.clear(); } } else // If not { std::ostringstream primal_out; std::ostringstream dual_out; primal_out << "primal_" << error_plot_suffix ; dual_out << "dual_" << error_plot_suffix ; primal_error_per_cell.plot_error(primal_out.str(), _system.get_mesh()); total_dual_error_per_cell.plot_error(dual_out.str(), _system.get_mesh()); primal_out.clear(); dual_out.clear(); } } // Weight the primal error by the dual error using the system norm object // FIXME: we ought to thread this for (unsigned int i=0; i != error_per_cell.size(); ++i) { // Have we been asked to weight the variable error contributions in any specific manner if(!error_norm_is_identity) // If we do { // Create Error Vectors to pass to calculate_norm std::vector<Real> cell_primal_error; std::vector<Real> cell_dual_error; for(unsigned int v = 0; v < n_vars; v++) { cell_primal_error.push_back((*primal_errors_per_cell[std::make_pair(&_system, v)])[i]); cell_dual_error.push_back((*total_dual_errors_per_cell[std::make_pair(&_system, v)])[i]); } error_per_cell[i] = static_cast<ErrorVectorReal> (error_norm.calculate_norm(cell_primal_error, cell_dual_error)); } else // If not { error_per_cell[i] = primal_error_per_cell[i]*total_dual_error_per_cell[i]; } } // Deallocate the ErrorMap contents if we allocated them earlier if (!error_norm_is_identity) for(unsigned int v = 0; v < n_vars; v++) { delete primal_errors_per_cell[std::make_pair(&_system, v)]; delete dual_errors_per_cell[std::make_pair(&_system, v)]; delete total_dual_errors_per_cell[std::make_pair(&_system, v)]; } }