Esempio n. 1
0
void
MaterialPropertyStorage::restrictStatefulProps(
    const std::vector<std::pair<unsigned int, QpMap>> & coarsening_map,
    const std::vector<const Elem *> & coarsened_element_children,
    QBase & qrule,
    QBase & qrule_face,
    MaterialData & material_data,
    const Elem & elem,
    int input_side)
{
  unsigned int side;

  bool doing_a_side = input_side != -1;

  unsigned int n_qpoints = 0;

  if (!doing_a_side)
  {
    side = 0; // Use 0 for the elem
    n_qpoints = qrule.n_points();
  }
  else
  {
    side = input_side;
    n_qpoints = qrule_face.n_points();
  }

  initProps(material_data, elem, side, n_qpoints);

  // Copy from the child stateful properties
  for (unsigned int qp = 0; qp < coarsening_map.size(); qp++)
  {
    const std::pair<unsigned int, QpMap> & qp_pair = coarsening_map[qp];
    unsigned int child = qp_pair.first;

    mooseAssert(child < coarsened_element_children.size(),
                "Coarsened element children vector not initialized");
    const Elem * child_elem = coarsened_element_children[child];
    const QpMap & qp_map = qp_pair.second;

    for (unsigned int i = 0; i < _stateful_prop_id_to_prop_id.size(); ++i)
    {
      mooseAssert(props().contains(child_elem),
                  "Child element pointer is not in the MaterialProps data structure");

      PropertyValue * child_property = props(child_elem, side)[i];
      PropertyValue * parent_property = props(&elem, side)[i];

      parent_property->qpCopy(qp, child_property, qp_map._to);

      propsOld(&elem, side)[i]->qpCopy(qp, propsOld(child_elem, side)[i], qp_map._to);
      if (hasOlderProperties())
        propsOlder(&elem, side)[i]->qpCopy(qp, propsOlder(child_elem, side)[i], qp_map._to);
    }
  }
}
Esempio n. 2
0
void
MaxQpsThread::operator() (const ConstElemRange & range)
{
    ParallelUniqueId puid;
    _tid = puid.id;

    // For short circuiting reinit
    std::set<ElemType> seen_it;
    for (ConstElemRange::const_iterator elem_it = range.begin() ; elem_it != range.end(); ++elem_it)
    {
        const Elem * elem = *elem_it;

        // Only reinit if the element type has not previously been seen
        if (seen_it.insert(elem->type()).second)
        {
            FEType fe_type(FIRST, LAGRANGE);
            unsigned int dim = elem->dim();
            unsigned int side = 0;           // we assume that any element will have at least one side ;)

            // We cannot mess with the FE objects in Assembly, because we might need to request second derivatives
            // later on. If we used them, we'd call reinit on them, thus making the call to request second
            // derivatives harmful (i.e. leading to segfaults/asserts). Thus, we have to use a locally allocated object here.
            FEBase * fe = FEBase::build(dim, fe_type).release();

            // figure out the number of qps for volume
            QBase * qrule = QBase::build(_qtype, dim, _order).release();
            fe->attach_quadrature_rule(qrule);
            fe->reinit(elem);
            if (qrule->n_points() > _max)
                _max = qrule->n_points();
            delete qrule;

            // figure out the number of qps for the face
            // NOTE: user might specify higher order rule for faces, thus possibly ending up with more qps than in the volume
            QBase * qrule_face = QBase::build(_qtype, dim - 1, _face_order).release();
            fe->attach_quadrature_rule(qrule_face);
            fe->reinit(elem, side);
            if (qrule_face->n_points() > _max)
                _max = qrule_face->n_points();
            delete qrule_face;

            delete fe;
        }
    }
}
Esempio n. 3
0
void QBase::tensor_product_quad(const QBase& q1D)
{

  const unsigned int n_points = q1D.n_points();

  _points.resize(n_points * n_points);

  _weights.resize(n_points * n_points);

  unsigned int qp=0;

  for (unsigned int j=0; j<n_points; j++)
    for (unsigned int i=0; i<n_points; i++)
      {
	_points[qp](0) = q1D.qp(i)(0);
	_points[qp](1) = q1D.qp(j)(0);

	_weights[qp] = q1D.w(i)*q1D.w(j);

	qp++;
      }
}
Esempio n. 4
0
void QBase::tensor_product_quad(const QBase& q1D)
{

  const unsigned int np = q1D.n_points();

  _points.resize(np * np);

  _weights.resize(np * np);

  unsigned int q=0;

  for (unsigned int j=0; j<np; j++)
    for (unsigned int i=0; i<np; i++)
      {
        _points[q](0) = q1D.qp(i)(0);
        _points[q](1) = q1D.qp(j)(0);

        _weights[q] = q1D.w(i)*q1D.w(j);

        q++;
      }
}
Esempio n. 5
0
void QBase::tensor_product_prism(const QBase& q1D, const QBase& q2D)
{
  const unsigned int n_points1D = q1D.n_points();
  const unsigned int n_points2D = q2D.n_points();

  _points.resize  (n_points1D * n_points2D);
  _weights.resize (n_points1D * n_points2D);

  unsigned int qp=0;

  for (unsigned int j=0; j<n_points1D; j++)
    for (unsigned int i=0; i<n_points2D; i++)
      {
	_points[qp](0) = q2D.qp(i)(0);
	_points[qp](1) = q2D.qp(i)(1);
	_points[qp](2) = q1D.qp(j)(0);

	_weights[qp] = q2D.w(i) * q1D.w(j);

	qp++;
      }

}
void
MaterialPropertyStorage::prolongStatefulProps(const std::vector<std::vector<QpMap> > & refinement_map,
                                              QBase & qrule,
                                              QBase & qrule_face,
                                              MaterialPropertyStorage & parent_material_props,
                                              MaterialData & child_material_data,
                                              const Elem & elem,
                                              const int input_parent_side,
                                              const int input_child,
                                              const int input_child_side)
{
  mooseAssert(input_child != -1 || input_parent_side == input_child_side, "Invalid inputs!");

  unsigned int n_qpoints = 0;

  // If we passed in -1 for these then we really need to store properties at 0
  unsigned int parent_side = input_parent_side == -1 ? 0 : input_parent_side;
  unsigned int child_side  = input_child_side  == -1 ? 0 : input_child_side;

  if (input_child_side == -1) // Not doing side projection (ie, doing volume projection)
    n_qpoints = qrule.n_points();
  else
    n_qpoints = qrule_face.n_points();

  child_material_data.size(n_qpoints);

  unsigned int n_children = elem.n_children();

  std::vector<unsigned int> children;

  if (input_child != -1) // Passed in a child explicitly
    children.push_back(input_child);
  else
  {
    children.resize(n_children);
    for (unsigned int child=0; child < n_children; child++)
      children[child] = child;
  }

  for (const auto & child : children)
  {
    // If we're not projecting an internal child side, but we are projecting sides, see if this child is on that side
    if (input_child == -1 && input_child_side != -1 && !elem.is_child_on_side(child, parent_side))
      continue;

    const Elem * child_elem = elem.child(child);

    mooseAssert(child < refinement_map.size(), "Refinement_map vector not initialized");
    const std::vector<QpMap> & child_map = refinement_map[child];

    if (props()[child_elem][child_side].size() == 0) props()[child_elem][child_side].resize(_stateful_prop_id_to_prop_id.size());
    if (propsOld()[child_elem][child_side].size() == 0) propsOld()[child_elem][child_side].resize(_stateful_prop_id_to_prop_id.size());
    if (propsOlder()[child_elem][child_side].size() == 0) propsOlder()[child_elem][child_side].resize(_stateful_prop_id_to_prop_id.size());

    // init properties (allocate memory. etc)
    for (unsigned int i=0; i < _stateful_prop_id_to_prop_id.size(); ++i)
    {
      // duplicate the stateful property in property storage (all three states - we will reuse the allocated memory there)
      // also allocating the right amount of memory, so we do not have to resize, etc.
      if (props()[child_elem][child_side][i] == NULL) props()[child_elem][child_side][i] = child_material_data.props()[ _stateful_prop_id_to_prop_id[i] ]->init(n_qpoints);
      if (propsOld()[child_elem][child_side][i] == NULL) propsOld()[child_elem][child_side][i] = child_material_data.propsOld()[ _stateful_prop_id_to_prop_id[i] ]->init(n_qpoints);
      if (hasOlderProperties())
        if (propsOlder()[child_elem][child_side][i] == NULL) propsOlder()[child_elem][child_side][i] = child_material_data.propsOlder()[ _stateful_prop_id_to_prop_id[i] ]->init(n_qpoints);

      // Copy from the parent stateful properties
      for (unsigned int qp=0; qp<refinement_map[child].size(); qp++)
      {
        PropertyValue * child_property = props()[child_elem][child_side][i];
        mooseAssert(props().contains(&elem), "Parent pointer is not in the MaterialProps data structure");
        PropertyValue * parent_property = parent_material_props.props()[&elem][parent_side][i];

        child_property->qpCopy(qp, parent_property, child_map[qp]._to);
        propsOld()[child_elem][child_side][i]->qpCopy(qp, parent_material_props.propsOld()[&elem][parent_side][i], child_map[qp]._to);
        if (hasOlderProperties())
          propsOlder()[child_elem][child_side][i]->qpCopy(qp, parent_material_props.propsOlder()[&elem][parent_side][i], child_map[qp]._to);
      }
    }
  }
}
void
MaterialPropertyStorage::restrictStatefulProps(const std::vector<std::pair<unsigned int, QpMap> > & coarsening_map,
                                               std::vector<const Elem *> & coarsened_element_children,
                                               QBase & qrule,
                                               QBase & qrule_face,
                                               MaterialData & material_data,
                                               const Elem & elem,
                                               int input_side)
{
  unsigned int side;

  bool doing_a_side = input_side != -1;

  unsigned int n_qpoints = 0;

  if (!doing_a_side)
  {
    side = 0; // Use 0 for the elem
    n_qpoints = qrule.n_points();
  }
  else
  {
    side = input_side;
    n_qpoints = qrule_face.n_points();
  }

  material_data.size(n_qpoints);

  // First, make sure that storage has been set aside for this element.
  //initStatefulProps(material_data, mats, n_qpoints, elem, side);

  if (props()[&elem][side].size() == 0) props()[&elem][side].resize(_stateful_prop_id_to_prop_id.size());
  if (propsOld()[&elem][side].size() == 0) propsOld()[&elem][side].resize(_stateful_prop_id_to_prop_id.size());
  if (propsOlder()[&elem][side].size() == 0) propsOlder()[&elem][side].resize(_stateful_prop_id_to_prop_id.size());

  // init properties (allocate memory. etc)
  for (unsigned int i=0; i < _stateful_prop_id_to_prop_id.size(); ++i)
  {
    // duplicate the stateful property in property storage (all three states - we will reuse the allocated memory there)
    // also allocating the right amount of memory, so we do not have to resize, etc.
    if (props()[&elem][side][i] == NULL) props()[&elem][side][i] = material_data.props()[ _stateful_prop_id_to_prop_id[i] ]->init(n_qpoints);
    if (propsOld()[&elem][side][i] == NULL) propsOld()[&elem][side][i] = material_data.propsOld()[ _stateful_prop_id_to_prop_id[i] ]->init(n_qpoints);
    if (hasOlderProperties())
      if (propsOlder()[&elem][side][i] == NULL) propsOlder()[&elem][side][i] = material_data.propsOlder()[ _stateful_prop_id_to_prop_id[i] ]->init(n_qpoints);
  }

  // Copy from the child stateful properties
  for (unsigned int qp=0; qp<coarsening_map.size(); qp++)
  {
    const std::pair<unsigned int, QpMap> & qp_pair = coarsening_map[qp];
    unsigned int child = qp_pair.first;

    mooseAssert(child < coarsened_element_children.size(), "Coarsened element children vector not initialized");
    const Elem * child_elem = coarsened_element_children[child];
    const QpMap & qp_map = qp_pair.second;

    for (unsigned int i=0; i < _stateful_prop_id_to_prop_id.size(); ++i)
    {
      mooseAssert(props().contains(child_elem), "Child element pointer is not in the MaterialProps data structure");

      PropertyValue * child_property = props()[child_elem][side][i];
      PropertyValue * parent_property = props()[&elem][side][i];

      parent_property->qpCopy(qp, child_property, qp_map._to);

      propsOld()[&elem][side][i]->qpCopy(qp, propsOld()[child_elem][side][i], qp_map._to);
      if (hasOlderProperties())
        propsOlder()[&elem][side][i]->qpCopy(qp, propsOlder()[child_elem][side][i], qp_map._to);
    }
  }
}
Esempio n. 8
0
void ExactSolution::_compute_error(const std::string & sys_name,
                                   const std::string & unknown_name,
                                   std::vector<Real> & error_vals)
{
    // Make sure we aren't "overconfigured"
    libmesh_assert (!(_exact_values.size() && _equation_systems_fine));

    // We need a commmunicator.
    const Parallel::Communicator & communicator(_equation_systems.comm());

    // This function must be run on all processors at once
    libmesh_parallel_only(communicator);

    // Get a reference to the system whose error is being computed.
    // If we have a fine grid, however, we'll integrate on that instead
    // for more accuracy.
    const System & computed_system = _equation_systems_fine ?
                                     _equation_systems_fine->get_system(sys_name) :
                                     _equation_systems.get_system (sys_name);

    const Real time = _equation_systems.get_system(sys_name).time;

    const unsigned int sys_num = computed_system.number();
    const unsigned int var = computed_system.variable_number(unknown_name);
    const unsigned int var_component =
        computed_system.variable_scalar_number(var, 0);

    // Prepare a global solution and a MeshFunction of the coarse system if we need one
    UniquePtr<MeshFunction> coarse_values;
    UniquePtr<NumericVector<Number> > comparison_soln = NumericVector<Number>::build(_equation_systems.comm());
    if (_equation_systems_fine)
    {
        const System & comparison_system
            = _equation_systems.get_system(sys_name);

        std::vector<Number> global_soln;
        comparison_system.update_global_solution(global_soln);
        comparison_soln->init(comparison_system.solution->size(), true, SERIAL);
        (*comparison_soln) = global_soln;

        coarse_values = UniquePtr<MeshFunction>
                        (new MeshFunction(_equation_systems,
                                          *comparison_soln,
                                          comparison_system.get_dof_map(),
                                          comparison_system.variable_number(unknown_name)));
        coarse_values->init();
    }

    // Initialize any functors we're going to use
    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();

    // Get a reference to the dofmap and mesh for that system
    const DofMap & computed_dof_map = computed_system.get_dof_map();

    const MeshBase & _mesh = computed_system.get_mesh();

    // Grab which element dimensions are present in the mesh
    const std::set<unsigned char> & elem_dims = _mesh.elem_dimensions();

    // Zero the error before summation
    // 0 - sum of square of function error (L2)
    // 1 - sum of square of gradient error (H1 semi)
    // 2 - sum of square of Hessian error (H2 semi)
    // 3 - sum of sqrt(square of function error) (L1)
    // 4 - max of sqrt(square of function error) (Linfty)
    // 5 - sum of square of curl error (HCurl semi)
    // 6 - sum of square of div error (HDiv semi)
    error_vals = std::vector<Real>(7, 0.);

    // Construct Quadrature rule based on default quadrature order
    const FEType & fe_type  = computed_dof_map.variable_type(var);

    unsigned int n_vec_dim = FEInterface::n_vec_dim( _mesh, fe_type );

    // FIXME: MeshFunction needs to be updated to support vector-valued
    //        elements before we can use a reference solution.
    if( (n_vec_dim > 1) && _equation_systems_fine )
    {
        libMesh::err << "Error calculation using reference solution not yet\n"
                     << "supported for vector-valued elements."
                     << std::endl;
        libmesh_not_implemented();
    }


    // Allow space for dims 0-3, even if we don't use them all
    std::vector<FEGenericBase<OutputShape> *> fe_ptrs(4, libmesh_nullptr);
    std::vector<QBase *> q_rules(4, libmesh_nullptr);

    // Prepare finite elements for each dimension present in the mesh
    for( std::set<unsigned char>::const_iterator d_it = elem_dims.begin();
            d_it != elem_dims.end(); ++d_it )
    {
        q_rules[*d_it] =
            fe_type.default_quadrature_rule (*d_it, _extra_order).release();

        // Construct finite element object

        fe_ptrs[*d_it] = FEGenericBase<OutputShape>::build(*d_it, fe_type).release();

        // Attach quadrature rule to FE object
        fe_ptrs[*d_it]->attach_quadrature_rule (q_rules[*d_it]);
    }

    // The global degree of freedom indices associated
    // with the local degrees of freedom.
    std::vector<dof_id_type> dof_indices;


    //
    // Begin the loop over the elements
    //
    // TODO: this ought to be threaded (and using subordinate
    // MeshFunction objects in each thread rather than a single
    // master)
    MeshBase::const_element_iterator       el     = _mesh.active_local_elements_begin();
    const MeshBase::const_element_iterator end_el = _mesh.active_local_elements_end();

    for ( ; el != end_el; ++el)
    {
        // Store a pointer to the element we are currently
        // working on.  This allows for nicer syntax later.
        const Elem * elem = *el;
        const unsigned int dim = elem->dim();

        const subdomain_id_type elem_subid = elem->subdomain_id();

        // If the variable is not active on this subdomain, don't bother
        if(!computed_system.variable(var).active_on_subdomain(elem_subid))
            continue;

        /* If the variable is active, then we're going to restrict the
           MeshFunction evaluations to the current element subdomain.
           This is for cases such as mixed dimension meshes where we want
           to restrict the calculation to one particular domain. */
        std::set<subdomain_id_type> subdomain_id;
        subdomain_id.insert(elem_subid);

        FEGenericBase<OutputShape> * fe = fe_ptrs[dim];
        QBase * qrule = q_rules[dim];
        libmesh_assert(fe);
        libmesh_assert(qrule);

        // The Jacobian*weight at the quadrature points.
        const std::vector<Real> & JxW = fe->get_JxW();

        // The value of the shape functions at the quadrature points
        // i.e. phi(i) = phi_values[i][qp]
        const std::vector<std::vector<OutputShape> > &  phi_values = fe->get_phi();

        // The value of the shape function gradients at the quadrature points
        const std::vector<std::vector<typename FEGenericBase<OutputShape>::OutputGradient> > &
        dphi_values = fe->get_dphi();

        // The value of the shape function curls at the quadrature points
        // Only computed for vector-valued elements
        const std::vector<std::vector<typename FEGenericBase<OutputShape>::OutputShape> > * curl_values = libmesh_nullptr;

        // The value of the shape function divergences at the quadrature points
        // Only computed for vector-valued elements
        const std::vector<std::vector<typename FEGenericBase<OutputShape>::OutputDivergence> > * div_values = libmesh_nullptr;

        if( FEInterface::field_type(fe_type) == TYPE_VECTOR )
        {
            curl_values = &fe->get_curl_phi();
            div_values = &fe->get_div_phi();
        }

#ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
        // The value of the shape function second derivatives at the quadrature points
        const std::vector<std::vector<typename FEGenericBase<OutputShape>::OutputTensor> > &
        d2phi_values = fe->get_d2phi();
#endif

        // The XYZ locations (in physical space) of the quadrature points
        const std::vector<Point> & q_point = fe->get_xyz();

        // reinitialize the element-specific data
        // for the current element
        fe->reinit (elem);

        // Get the local to global degree of freedom maps
        computed_dof_map.dof_indices    (elem, dof_indices, var);

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

        // The number of shape functions
        const unsigned int n_sf =
            cast_int<unsigned int>(dof_indices.size());

        //
        // Begin the loop over the Quadrature points.
        //
        for (unsigned int qp=0; qp<n_qp; qp++)
        {
            // Real u_h = 0.;
            // RealGradient grad_u_h;

            typename FEGenericBase<OutputShape>::OutputNumber u_h(0.);

            typename FEGenericBase<OutputShape>::OutputNumberGradient grad_u_h;
#ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
            typename FEGenericBase<OutputShape>::OutputNumberTensor grad2_u_h;
#endif
            typename FEGenericBase<OutputShape>::OutputNumber curl_u_h(0.0);
            typename FEGenericBase<OutputShape>::OutputNumberDivergence div_u_h = 0.0;

            // Compute solution values at the current
            // quadrature point.  This reqiures a sum
            // over all the shape functions evaluated
            // at the quadrature point.
            for (unsigned int i=0; i<n_sf; i++)
            {
                // Values from current solution.
                u_h      += phi_values[i][qp]*computed_system.current_solution  (dof_indices[i]);
                grad_u_h += dphi_values[i][qp]*computed_system.current_solution (dof_indices[i]);
#ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
                grad2_u_h += d2phi_values[i][qp]*computed_system.current_solution (dof_indices[i]);
#endif
                if( FEInterface::field_type(fe_type) == TYPE_VECTOR )
                {
                    curl_u_h += (*curl_values)[i][qp]*computed_system.current_solution (dof_indices[i]);
                    div_u_h += (*div_values)[i][qp]*computed_system.current_solution (dof_indices[i]);
                }
            }

            // Compute the value of the error at this quadrature point
            typename FEGenericBase<OutputShape>::OutputNumber exact_val(0);
            RawAccessor<typename FEGenericBase<OutputShape>::OutputNumber> exact_val_accessor( exact_val, dim );
            if (_exact_values.size() > sys_num && _exact_values[sys_num])
            {
                for( unsigned int c = 0; c < n_vec_dim; c++)
                    exact_val_accessor(c) =
                        _exact_values[sys_num]->
                        component(var_component+c, q_point[qp], time);
            }
            else if (_equation_systems_fine)
            {
                // FIXME: Needs to be updated for vector-valued elements
                DenseVector<Number> output(1);
                (*coarse_values)(q_point[qp],time,output,&subdomain_id);
                exact_val = output(0);
            }
            const typename FEGenericBase<OutputShape>::OutputNumber val_error = u_h - exact_val;

            // Add the squares of the error to each contribution
            Real error_sq = TensorTools::norm_sq(val_error);
            error_vals[0] += JxW[qp]*error_sq;

            Real norm = sqrt(error_sq);
            error_vals[3] += JxW[qp]*norm;

            if(error_vals[4]<norm) {
                error_vals[4] = norm;
            }

            // Compute the value of the error in the gradient at this
            // quadrature point
            typename FEGenericBase<OutputShape>::OutputNumberGradient exact_grad;
            RawAccessor<typename FEGenericBase<OutputShape>::OutputNumberGradient> exact_grad_accessor( exact_grad, LIBMESH_DIM );
            if (_exact_derivs.size() > sys_num && _exact_derivs[sys_num])
            {
                for (unsigned int c = 0; c < n_vec_dim; c++)
                    for (unsigned int d = 0; d < LIBMESH_DIM; d++)
                        exact_grad_accessor(d + c*LIBMESH_DIM) =
                            _exact_derivs[sys_num]->
                            component(var_component+c, q_point[qp], time)(d);
            }
            else if (_equation_systems_fine)
            {
                // FIXME: Needs to be updated for vector-valued elements
                std::vector<Gradient> output(1);
                coarse_values->gradient(q_point[qp],time,output,&subdomain_id);
                exact_grad = output[0];
            }

            const typename FEGenericBase<OutputShape>::OutputNumberGradient grad_error = grad_u_h - exact_grad;

            error_vals[1] += JxW[qp]*grad_error.norm_sq();


            if( FEInterface::field_type(fe_type) == TYPE_VECTOR )
            {
                // Compute the value of the error in the curl at this
                // quadrature point
                typename FEGenericBase<OutputShape>::OutputNumber exact_curl(0.0);
                if (_exact_derivs.size() > sys_num && _exact_derivs[sys_num])
                {
                    exact_curl = TensorTools::curl_from_grad( exact_grad );
                }
                else if (_equation_systems_fine)
                {
                    // FIXME: Need to implement curl for MeshFunction and support reference
                    //        solution for vector-valued elements
                }

                const typename FEGenericBase<OutputShape>::OutputNumber curl_error = curl_u_h - exact_curl;

                error_vals[5] += JxW[qp]*TensorTools::norm_sq(curl_error);

                // Compute the value of the error in the divergence at this
                // quadrature point
                typename FEGenericBase<OutputShape>::OutputNumberDivergence exact_div = 0.0;
                if (_exact_derivs.size() > sys_num && _exact_derivs[sys_num])
                {
                    exact_div = TensorTools::div_from_grad( exact_grad );
                }
                else if (_equation_systems_fine)
                {
                    // FIXME: Need to implement div for MeshFunction and support reference
                    //        solution for vector-valued elements
                }

                const typename FEGenericBase<OutputShape>::OutputNumberDivergence div_error = div_u_h - exact_div;

                error_vals[6] += JxW[qp]*TensorTools::norm_sq(div_error);
            }

#ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
            // Compute the value of the error in the hessian at this
            // quadrature point
            typename FEGenericBase<OutputShape>::OutputNumberTensor exact_hess;
            RawAccessor<typename FEGenericBase<OutputShape>::OutputNumberTensor> exact_hess_accessor( exact_hess, dim );
            if (_exact_hessians.size() > sys_num && _exact_hessians[sys_num])
            {
                //FIXME: This needs to be implemented to support rank 3 tensors
                //       which can't happen until type_n_tensor is fully implemented
                //       and a RawAccessor<TypeNTensor> is fully implemented
                if( FEInterface::field_type(fe_type) == TYPE_VECTOR )
                    libmesh_not_implemented();

                for( unsigned int c = 0; c < n_vec_dim; c++)
                    for( unsigned int d = 0; d < dim; d++ )
                        for( unsigned int e =0; e < dim; e++ )
                            exact_hess_accessor(d + e*dim + c*dim*dim) =
                                _exact_hessians[sys_num]->
                                component(var_component+c, q_point[qp], time)(d,e);
            }
            else if (_equation_systems_fine)
            {
                // FIXME: Needs to be updated for vector-valued elements
                std::vector<Tensor> output(1);
                coarse_values->hessian(q_point[qp],time,output,&subdomain_id);
                exact_hess = output[0];
            }

            const typename FEGenericBase<OutputShape>::OutputNumberTensor grad2_error = grad2_u_h - exact_hess;

            // FIXME: PB: Is this what we want for rank 3 tensors?
            error_vals[2] += JxW[qp]*grad2_error.norm_sq();
#endif

        } // end qp loop
    } // end element loop

    // Clean up the FE and QBase pointers we created
    for( std::set<unsigned char>::const_iterator d_it = elem_dims.begin();
            d_it != elem_dims.end(); ++d_it )
    {
        delete fe_ptrs[*d_it];
        delete q_rules[*d_it];
    }

    // Add up the error values on all processors, except for the L-infty
    // norm, for which the maximum is computed.
    Real l_infty_norm = error_vals[4];
    communicator.max(l_infty_norm);
    communicator.sum(error_vals);
    error_vals[4] = l_infty_norm;
}