void
KellyErrorEstimator::internal_side_integration ()
{
  Real error = 1.e-30;
  unsigned int n_qp = fe_fine->n_quadrature_points();
  unsigned int n_fine_dofs = Ufine.size();
  unsigned int n_coarse_dofs = Ucoarse.size();

  std::vector<std::vector<RealGradient> > dphi_coarse = fe_coarse->get_dphi();
  std::vector<std::vector<RealGradient> > dphi_fine = fe_fine->get_dphi();
  std::vector<Point> face_normals = fe_fine->get_normals();
  std::vector<Real> JxW_face = fe_fine->get_JxW();

  for (unsigned int qp=0; qp != n_qp; ++qp)
    {
      // Calculate solution gradients on fine and coarse elements
      // at this quadrature point
      Gradient grad_fine, grad_coarse;
      for (unsigned int i=0; i != n_coarse_dofs; ++i)
        grad_coarse.add_scaled (dphi_coarse[i][qp], Ucoarse(i));

      for (unsigned int i=0; i != n_fine_dofs; ++i)
        grad_fine.add_scaled (dphi_fine[i][qp], Ufine(i));

      // Find the jump in the normal derivative
      // at this quadrature point
      const Number jump = (grad_fine - grad_coarse)*face_normals[qp];
      const Real jump2 = TensorTools::norm_sq(jump);

      // Accumulate the jump integral
      error += JxW_face[qp] * jump2;
    }

  // Add the h-weighted jump integral to each error term
  fine_error =
    error * fine_elem->hmax() * error_norm.weight(var);
  coarse_error =
    error * coarse_elem->hmax() * error_norm.weight(var);
}
void
DiscontinuityMeasure::internal_side_integration ()
{
  Real error = 1.e-30;
  unsigned int n_qp = fe_fine->n_quadrature_points();
  unsigned int n_fine_dofs = Ufine.size();
  unsigned int n_coarse_dofs = Ucoarse.size();

  std::vector<std::vector<Real> > phi_coarse = fe_coarse->get_phi();
  std::vector<std::vector<Real> > phi_fine = fe_fine->get_phi();
  std::vector<Real> JxW_face = fe_fine->get_JxW();

  for (unsigned int qp=0; qp != n_qp; ++qp)
    {
      // Calculate solution values on fine and coarse elements
      // at this quadrature point
      Number u_fine = 0., u_coarse = 0.;
      for (unsigned int i=0; i != n_coarse_dofs; ++i)
        u_coarse += phi_coarse[i][qp] * Ucoarse(i);

      for (unsigned int i=0; i != n_fine_dofs; ++i)
        u_fine += phi_fine[i][qp] * Ufine(i);

      // Find the jump in the value
      // at this quadrature point
      const Number jump = u_fine - u_coarse;
      const Real jump2 = libmesh_norm(jump);
      // Accumulate the jump integral
      error += JxW_face[qp] * jump2;
    }

  // Add the h-weighted jump integral to each error term
  fine_error =
    error * fine_elem->hmax() * error_norm.weight(var);
  coarse_error =
    error * coarse_elem->hmax() * error_norm.weight(var);
}
bool
DiscontinuityMeasure::boundary_side_integration ()
{
  const std::string &var_name = my_system->variable_name(var);

  std::vector<std::vector<Real> > phi_fine = fe_fine->get_phi();
  std::vector<Real> JxW_face = fe_fine->get_JxW();
  std::vector<Point> qface_point = fe_fine->get_xyz();

  // The reinitialization also recomputes the locations of
  // the quadrature points on the side.  By checking if the
  // first quadrature point on the side is on an essential boundary
  // for a particular variable, we will determine if the whole
  // element is on an essential boundary (assuming quadrature points
  // are strictly contained in the side).
  if (this->_bc_function(*my_system, qface_point[0], var_name).first)
    {
      const Real h = fine_elem->hmax();

      // The number of quadrature points
      const unsigned int n_qp = fe_fine->n_quadrature_points();

      // The error contribution from this face
      Real error = 1.e-30;

      // loop over the integration points on the face.
      for (unsigned int qp=0; qp<n_qp; qp++)
        {
          // Value of the imposed essential BC at this quadrature point.
          const std::pair<bool,Real> essential_bc =
            this->_bc_function(*my_system, qface_point[qp], var_name);

          // Be sure the BC function still thinks we're on the
          // essential boundary.
          libmesh_assert (essential_bc.first == true);

          // The solution gradient from each element
          Number u_fine = 0.;

          // Compute the solution gradient on element e
          for (unsigned int i=0; i != Ufine.size(); i++)
            u_fine += phi_fine[i][qp] * Ufine(i);

          // The difference between the desired BC and the approximate solution.
          const Number jump = essential_bc.second - u_fine;

          // The flux jump squared.  If using complex numbers,
          // libmesh_norm(z) returns |z|^2, where |z| is the modulus of z.
          const Real jump2 = libmesh_norm(jump);

          // Integrate the error on the face.  The error is
          // scaled by an additional power of h, where h is
          // the maximum side length for the element.  This
          // arises in the definition of the indicator.
          error += JxW_face[qp]*jump2;

        } // End quadrature point loop

      fine_error = error*h*error_norm.weight(var);

      return true;
    } // end if side on flux boundary
  return false;
}
Beispiel #4
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");
}