Exemplo n.º 1
0
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 AdjointRefinementEstimator::estimate_error (const System& _system,
					         ErrorVector& error_per_cell,
					         const NumericVector<Number>* solution_vector,
					         bool /*estimate_parent_error*/)
{
  // We have to break the rules here, because we can't refine a const System
  System& system = const_cast<System&>(_system);

  // An EquationSystems reference will be convenient.
  EquationSystems& es = system.get_equation_systems();

  // The current mesh
  MeshBase& mesh = es.get_mesh();

  // Resize the error_per_cell vector to be
  // the number of elements, initialized to 0.
  error_per_cell.clear();
  error_per_cell.resize (mesh.max_elem_id(), 0.);

  // We'll want to back up all coarse grid vectors
  std::map<std::string, NumericVector<Number> *> coarse_vectors;
  for (System::vectors_iterator vec = system.vectors_begin(); vec !=
       system.vectors_end(); ++vec)
    {
      // The (string) name of this vector
      const std::string& var_name = vec->first;

      coarse_vectors[var_name] = vec->second->clone().release();
    }
  // Back up the coarse solution and coarse local solution
  NumericVector<Number> * coarse_solution =
    system.solution->clone().release();
  NumericVector<Number> * coarse_local_solution =
    system.current_local_solution->clone().release();
  // And make copies of the projected solution
  NumericVector<Number> * projected_solution;

  // And we'll need to temporarily change solution projection settings
  bool old_projection_setting;
  old_projection_setting = system.project_solution_on_reinit();

  // Make sure the solution is projected when we refine the mesh
  system.project_solution_on_reinit() = true;

  // And it'll be best to avoid any repartitioning
  AutoPtr<Partitioner> old_partitioner = mesh.partitioner();
  mesh.partitioner().reset(NULL);

  // And we can't allow any renumbering
  const bool old_renumbering_setting = mesh.allow_renumbering();
  mesh.allow_renumbering(false);

  // Use a non-standard solution vector if necessary
  if (solution_vector && solution_vector != system.solution.get())
    {
      NumericVector<Number> *newsol =
        const_cast<NumericVector<Number>*> (solution_vector);
      newsol->swap(*system.solution);
      system.update();
    }

#ifndef NDEBUG
  // n_coarse_elem is only used in an assertion later so
  // avoid declaring it unless asserts are active.
  const dof_id_type n_coarse_elem = mesh.n_elem();
#endif

  // Uniformly refine the mesh
  MeshRefinement mesh_refinement(mesh);

  libmesh_assert (number_h_refinements > 0 || number_p_refinements > 0);

  // FIXME: this may break if there is more than one System
  // on this mesh but estimate_error was still called instead of
  // estimate_errors
  for (unsigned int i = 0; i != number_h_refinements; ++i)
    {
      mesh_refinement.uniformly_refine(1);
      es.reinit();
    }

  for (unsigned int i = 0; i != number_p_refinements; ++i)
    {
      mesh_refinement.uniformly_p_refine(1);
      es.reinit();
    }

  // Copy the projected coarse grid solutions, which will be
  // overwritten by solve()
  projected_solution = NumericVector<Number>::build(mesh.comm()).release();
  projected_solution->init(system.solution->size(), true, SERIAL);
  system.solution->localize(*projected_solution,
			    system.get_dof_map().get_send_list());

  // Rebuild the rhs with the projected primal solution
  (dynamic_cast<ImplicitSystem&>(system)).assembly(true, false);
  NumericVector<Number> & projected_residual = (dynamic_cast<ExplicitSystem&>(system)).get_vector("RHS Vector");
  projected_residual.close();

  // Solve the adjoint problem on the refined FE space
  system.adjoint_solve();

  // Now that we have the refined adjoint solution and the projected primal solution,
  // we first compute the global QoI error estimate

  // Resize the computed_global_QoI_errors vector to hold the error estimates for each QoI
  computed_global_QoI_errors.resize(system.qoi.size());

  // Loop over all the adjoint solutions and get the QoI error
  // contributions from all of them
  for (unsigned int j=0; j != system.qoi.size(); j++)
    {
      computed_global_QoI_errors[j] = projected_residual.dot(system.get_adjoint_solution(j));
    }

  // Done with the global error estimates, now construct the element wise error indicators

  // We ought to account for 'spill-over' effects while computing the
  // element error indicators This happens because the same dof is
  // shared by multiple elements, one way of mitigating this is to
  // scale the contribution from each dof by the number of elements it
  // belongs to We first obtain the number of elements each node
  // belongs to

  // A map that relates a node id to an int that will tell us how many elements it is a node of
  LIBMESH_BEST_UNORDERED_MAP<dof_id_type, unsigned int>shared_element_count;

  // To fill this map, we will loop over elements, and then in each element, we will loop
  // over the nodes each element contains, and then query it for the number of coarse
  // grid elements it was a node of

  // We will be iterating over all the active elements in the fine 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();

  // Keep track of which nodes we have already dealt with
  LIBMESH_BEST_UNORDERED_SET<dof_id_type> processed_node_ids;

  // Start loop over elems
  for(; elem_it != elem_end; ++elem_it)
    {
      // Pointer to this element
      const Elem* elem = *elem_it;

      // Loop over the nodes in the element
      for(unsigned int n=0; n != elem->n_nodes(); ++n)
	{
	  // Get a pointer to the current node
	  Node* node = elem->get_node(n);

	  // Get the id of this node
	  dof_id_type node_id = node->id();

	  // If we havent already processed this node, do so now
	  if(processed_node_ids.find(node_id) == processed_node_ids.end())
	    {
	      // Declare a neighbor_set to be filled by the find_point_neighbors
	      std::set<const Elem *> fine_grid_neighbor_set;

	      // Call find_point_neighbors to fill the neighbor_set
	      elem->find_point_neighbors(*node, fine_grid_neighbor_set);

	      // A vector to hold the coarse grid parents neighbors
	      std::vector<dof_id_type> coarse_grid_neighbors;

	      // Iterators over the fine grid neighbors set
	      std::set<const Elem*>::iterator fine_neighbor_it = fine_grid_neighbor_set.begin();
	      const std::set<const Elem*>::iterator fine_neighbor_end = fine_grid_neighbor_set.end();

	      // Loop over all the fine neighbors of this node
	      for(; fine_neighbor_it != fine_neighbor_end ; ++fine_neighbor_it)
		{
		  // Pointer to the current fine neighbor element
		  const Elem* fine_elem = *fine_neighbor_it;

		  // Find the element id for the corresponding coarse grid element
		  const Elem* coarse_elem = fine_elem;
		  for (unsigned int j = 0; j != number_h_refinements; ++j)
		    {
		      libmesh_assert (coarse_elem->parent());

		      coarse_elem = coarse_elem->parent();
		    }

		  // Loop over the existing coarse neighbors and check if this one is
		  // already in there
                  const dof_id_type coarse_id = coarse_elem->id();
		  std::size_t j = 0;
		  for (; j != coarse_grid_neighbors.size(); j++)
		    {
		      // If the set already contains this element break out of the loop
		      if(coarse_grid_neighbors[j] == coarse_id)
			{
			  break;
			}
		    }

		  // If we didn't leave the loop even at the last element,
		  // this is a new neighbour, put in the coarse_grid_neighbor_set
		  if(j == coarse_grid_neighbors.size())
		    {
		      coarse_grid_neighbors.push_back(coarse_id);
		    }

		} // End loop over fine neighbors

	      // Set the shared_neighbour index for this node to the
	      // size of the coarse grid neighbor set
	      shared_element_count[node_id] =
	        libmesh_cast_int<unsigned int>(coarse_grid_neighbors.size());

	      // Add this node to processed_node_ids vector
	      processed_node_ids.insert(node_id);

	    } // End if not processed node

	} // End loop over nodes

    }  // End loop over elems

  // Get a DoF map, will be used to get the nodal dof_indices for each element
  DofMap &dof_map = system.get_dof_map();

  // The global DOF indices, we will use these later on when we compute the element wise indicators
  std::vector<dof_id_type> dof_indices;

  // Localize the global rhs and adjoint solution vectors (which might be shared on multiple processsors) onto a
  // local ghosted vector, this ensures each processor has all the dof_indices to compute an error indicator for
  // an element it owns
  AutoPtr<NumericVector<Number> > localized_projected_residual = NumericVector<Number>::build(system.comm());
  localized_projected_residual->init(system.n_dofs(), system.n_local_dofs(), system.get_dof_map().get_send_list(), false, GHOSTED);
  projected_residual.localize(*localized_projected_residual, system.get_dof_map().get_send_list());

  // Each adjoint solution will also require ghosting; for efficiency we'll reuse the same memory
  AutoPtr<NumericVector<Number> > localized_adjoint_solution = NumericVector<Number>::build(system.comm());
  localized_adjoint_solution->init(system.n_dofs(), system.n_local_dofs(), system.get_dof_map().get_send_list(), false, GHOSTED);

  // We will loop over each adjoint solution, localize that adjoint
  // solution and then loop over local elements
  for (unsigned int i=0; i != system.qoi.size(); i++)
    {
      // Skip this QoI if not in the QoI Set
      if (_qoi_set.has_index(i))
	{
	  // Get the weight for the current QoI
	  Real error_weight = _qoi_set.weight(i);

	  (system.get_adjoint_solution(i)).localize(*localized_adjoint_solution, system.get_dof_map().get_send_list());

	  // Loop over elements
          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)
	    {
	      // Pointer to the element
	      const Elem* elem = *elem_it;

	      // Go up number_h_refinements levels up to find the coarse parent
	      const Elem* coarse = elem;

	      for (unsigned int j = 0; j != number_h_refinements; ++j)
	        {
		  libmesh_assert (coarse->parent());

		  coarse = coarse->parent();
		}

              const dof_id_type e_id = coarse->id();

	      // Get the local to global degree of freedom maps for this element
	      dof_map.dof_indices (elem, dof_indices);

	      // We will have to manually do the dot products.
              Number local_contribution = 0.;

	      for (unsigned int j=0; j != dof_indices.size(); j++)
		{
		  // The contribution to the error indicator for this element from the current QoI
		  local_contribution += (*localized_projected_residual)(dof_indices[j]) * (*localized_adjoint_solution)(dof_indices[j]);
		}

	      // Multiply by the error weight for this QoI
              local_contribution *= error_weight;

              // FIXME: we're throwing away information in the
              // --enable-complex case
	      error_per_cell[e_id] += static_cast<ErrorVectorReal>
	        (libmesh_real(local_contribution));

	    } // End loop over elements

	} // End if belong to QoI set

    } // End loop over QoIs

  // Don't bother projecting the solution; we'll restore from backup
  // after coarsening
  system.project_solution_on_reinit() = false;

  // Uniformly coarsen the mesh, without projecting the solution
  libmesh_assert (number_h_refinements > 0 || number_p_refinements > 0);

  for (unsigned int i = 0; i != number_h_refinements; ++i)
    {
      mesh_refinement.uniformly_coarsen(1);
      // FIXME - should the reinits here be necessary? - RHS
      es.reinit();
    }

  for (unsigned int i = 0; i != number_p_refinements; ++i)
    {
      mesh_refinement.uniformly_p_coarsen(1);
      es.reinit();
    }

  // We should be back where we started
  libmesh_assert_equal_to (n_coarse_elem, mesh.n_elem());

  // Restore old solutions and clean up the heap
  system.project_solution_on_reinit() = old_projection_setting;

  // Restore the coarse solution vectors and delete their copies
  *system.solution = *coarse_solution;
  delete coarse_solution;
  *system.current_local_solution = *coarse_local_solution;
  delete coarse_local_solution;
  delete projected_solution;

  for (System::vectors_iterator vec = system.vectors_begin(); vec !=
	 system.vectors_end(); ++vec)
    {
      // The (string) name of this vector
      const std::string& var_name = vec->first;

      // If it's a vector we already had (and not a newly created
      // vector like an adjoint rhs), we need to restore it.
      std::map<std::string, NumericVector<Number> *>::iterator it =
        coarse_vectors.find(var_name);
      if (it != coarse_vectors.end())
        {
          NumericVector<Number> *coarsevec = it->second;
          system.get_vector(var_name) = *coarsevec;

          coarsevec->clear();
          delete coarsevec;
        }
    }

  // Restore old partitioner and renumbering settings
  mesh.partitioner() = old_partitioner;
  mesh.allow_renumbering(old_renumbering_setting);

  // Fiinally sum the vector of estimated error values.
  this->reduce_error(error_per_cell, system.comm());

  // We don't take a square root here; this is a goal-oriented
  // estimate not a Hilbert norm estimate.
} // end estimate_error function
Exemplo n.º 3
0
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");
}
Exemplo n.º 4
0
  void ExactErrorEstimator::estimate_error (const System& system,
                                            ErrorVector& error_per_cell,
                                            const NumericVector<Number>* solution_vector,
                                            bool /* estimate_parent_error */ )
#endif
{
  // 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);

      AutoPtr<FEBase> fe (FEBase::build (dim, fe_type));

      // Build an appropriate Gaussian quadrature rule
      AutoPtr<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
      AutoPtr<MeshFunction> fine_values;
      AutoPtr<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 (global_soln.size(), true, SERIAL);
          (*fine_soln) = global_soln;

          fine_values = AutoPtr<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(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 =
            libmesh_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.
  START_LOG("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]);
        }


    }
  STOP_LOG("std::sqrt()", "ExactErrorEstimator");

  // 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();
    }
}