1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2020 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 
18 // Local includes
19 #include "libmesh/dof_map.h"
20 #include "libmesh/elem.h"
21 #include "libmesh/exact_solution.h"
22 #include "libmesh/equation_systems.h"
23 #include "libmesh/fe_base.h"
24 #include "libmesh/function_base.h"
25 #include "libmesh/mesh_base.h"
26 #include "libmesh/mesh_function.h"
27 #include "libmesh/numeric_vector.h"
28 #include "libmesh/parallel.h"
29 #include "libmesh/quadrature.h"
30 #include "libmesh/wrapped_function.h"
31 #include "libmesh/fe_interface.h"
32 #include "libmesh/raw_accessor.h"
33 #include "libmesh/tensor_tools.h"
34 #include "libmesh/enum_norm_type.h"
35 #include "libmesh/utility.h"
36 #include "libmesh/auto_ptr.h" // libmesh_make_unique
37 
38 namespace libMesh
39 {
40 
ExactSolution(const EquationSystems & es)41 ExactSolution::ExactSolution(const EquationSystems & es) :
42   _equation_systems(es),
43   _equation_systems_fine(nullptr),
44   _extra_order(0)
45 {
46   // Initialize the _errors data structure which holds all
47   // the eventual values of the error.
48   for (auto sys : make_range(_equation_systems.n_systems()))
49     {
50       // Reference to the system
51       const System & system = _equation_systems.get_system(sys);
52 
53       // The name of the system
54       const std::string & sys_name = system.name();
55 
56       // The SystemErrorMap to be inserted
57       ExactSolution::SystemErrorMap sem;
58 
59       for (auto var : make_range(system.n_vars()))
60         {
61           // The name of this variable
62           const std::string & var_name = system.variable_name(var);
63           sem[var_name] = std::vector<Real>(5, 0.);
64         }
65 
66       _errors[sys_name] = sem;
67     }
68 }
69 
70 
71 ExactSolution::ExactSolution(ExactSolution &&) = default;
72 ExactSolution::~ExactSolution() = default;
73 
74 
75 void ExactSolution::
set_excluded_subdomains(const std::set<subdomain_id_type> & excluded)76 set_excluded_subdomains(const std::set<subdomain_id_type> & excluded)
77 {
78   _excluded_subdomains = excluded;
79 }
80 
attach_reference_solution(const EquationSystems * es_fine)81 void ExactSolution::attach_reference_solution (const EquationSystems * es_fine)
82 {
83   libmesh_assert(es_fine);
84   _equation_systems_fine = es_fine;
85 
86   // If we're using a fine grid solution, we're not using exact values
87   _exact_values.clear();
88   _exact_derivs.clear();
89   _exact_hessians.clear();
90 }
91 
92 
attach_exact_value(ValueFunctionPointer fptr)93 void ExactSolution::attach_exact_value (ValueFunctionPointer fptr)
94 {
95   libmesh_assert(fptr);
96 
97   // Clear out any previous _exact_values entries, then add a new
98   // entry for each system.
99   _exact_values.clear();
100 
101   for (auto sys : make_range(_equation_systems.n_systems()))
102     {
103       const System & system = _equation_systems.get_system(sys);
104       _exact_values.emplace_back(libmesh_make_unique<WrappedFunction<Number>>(system, fptr, &_equation_systems.parameters));
105     }
106 
107   // If we're using exact values, we're not using a fine grid solution
108   _equation_systems_fine = nullptr;
109 }
110 
111 
attach_exact_values(const std::vector<FunctionBase<Number> * > & f)112 void ExactSolution::attach_exact_values (const std::vector<FunctionBase<Number> *> & f)
113 {
114   // Automatically delete any previous _exact_values entries, then add a new
115   // entry for each system.
116   _exact_values.clear();
117 
118   for (auto ptr : f)
119     _exact_values.emplace_back(ptr ? ptr->clone() : nullptr);
120 }
121 
122 
attach_exact_value(unsigned int sys_num,FunctionBase<Number> * f)123 void ExactSolution::attach_exact_value (unsigned int sys_num,
124                                         FunctionBase<Number> * f)
125 {
126   if (_exact_values.size() <= sys_num)
127     _exact_values.resize(sys_num+1);
128 
129   if (f)
130     _exact_values[sys_num] = f->clone();
131 }
132 
133 
attach_exact_deriv(GradientFunctionPointer gptr)134 void ExactSolution::attach_exact_deriv (GradientFunctionPointer gptr)
135 {
136   libmesh_assert(gptr);
137 
138   // Clear out any previous _exact_derivs entries, then add a new
139   // entry for each system.
140   _exact_derivs.clear();
141 
142   for (auto sys : make_range(_equation_systems.n_systems()))
143     {
144       const System & system = _equation_systems.get_system(sys);
145       _exact_derivs.emplace_back(libmesh_make_unique<WrappedFunction<Gradient>>(system, gptr, &_equation_systems.parameters));
146     }
147 
148   // If we're using exact values, we're not using a fine grid solution
149   _equation_systems_fine = nullptr;
150 }
151 
152 
attach_exact_derivs(const std::vector<FunctionBase<Gradient> * > & g)153 void ExactSolution::attach_exact_derivs (const std::vector<FunctionBase<Gradient> *> & g)
154 {
155   // Automatically delete any previous _exact_derivs entries, then add a new
156   // entry for each system.
157   _exact_derivs.clear();
158 
159   for (auto ptr : g)
160     _exact_derivs.emplace_back(ptr ? ptr->clone() : nullptr);
161 }
162 
163 
attach_exact_deriv(unsigned int sys_num,FunctionBase<Gradient> * g)164 void ExactSolution::attach_exact_deriv (unsigned int sys_num,
165                                         FunctionBase<Gradient> * g)
166 {
167   if (_exact_derivs.size() <= sys_num)
168     _exact_derivs.resize(sys_num+1);
169 
170   if (g)
171     _exact_derivs[sys_num] = g->clone();
172 }
173 
174 
attach_exact_hessian(HessianFunctionPointer hptr)175 void ExactSolution::attach_exact_hessian (HessianFunctionPointer hptr)
176 {
177   libmesh_assert(hptr);
178 
179   // Clear out any previous _exact_hessians entries, then add a new
180   // entry for each system.
181   _exact_hessians.clear();
182 
183   for (auto sys : make_range(_equation_systems.n_systems()))
184     {
185       const System & system = _equation_systems.get_system(sys);
186       _exact_hessians.emplace_back(libmesh_make_unique<WrappedFunction<Tensor>>(system, hptr, &_equation_systems.parameters));
187     }
188 
189   // If we're using exact values, we're not using a fine grid solution
190   _equation_systems_fine = nullptr;
191 }
192 
193 
attach_exact_hessians(std::vector<FunctionBase<Tensor> * > h)194 void ExactSolution::attach_exact_hessians (std::vector<FunctionBase<Tensor> *> h)
195 {
196   // Automatically delete any previous _exact_hessians entries, then add a new
197   // entry for each system.
198   _exact_hessians.clear();
199 
200   for (auto ptr : h)
201     _exact_hessians.emplace_back(ptr ? ptr->clone() : nullptr);
202 }
203 
204 
attach_exact_hessian(unsigned int sys_num,FunctionBase<Tensor> * h)205 void ExactSolution::attach_exact_hessian (unsigned int sys_num,
206                                           FunctionBase<Tensor> * h)
207 {
208   if (_exact_hessians.size() <= sys_num)
209     _exact_hessians.resize(sys_num+1);
210 
211   if (h)
212     _exact_hessians[sys_num] = h->clone();
213 }
214 
215 
_check_inputs(const std::string & sys_name,const std::string & unknown_name)216 std::vector<Real> & ExactSolution::_check_inputs(const std::string & sys_name,
217                                                  const std::string & unknown_name)
218 {
219   // Return a reference to the proper error entry, or throw an error
220   // if it doesn't exist.
221   auto & system_error_map = libmesh_map_find(_errors, sys_name);
222   return libmesh_map_find(system_error_map, unknown_name);
223 }
224 
225 
226 
compute_error(const std::string & sys_name,const std::string & unknown_name)227 void ExactSolution::compute_error(const std::string & sys_name,
228                                   const std::string & unknown_name)
229 {
230   // Check the inputs for validity, and get a reference
231   // to the proper location to store the error
232   std::vector<Real> & error_vals = this->_check_inputs(sys_name,
233                                                        unknown_name);
234 
235   libmesh_assert( _equation_systems.has_system(sys_name) );
236   const System & sys = _equation_systems.get_system<System>( sys_name );
237 
238   libmesh_assert( sys.has_variable( unknown_name ) );
239   switch( FEInterface::field_type(sys.variable_type( unknown_name )) )
240     {
241     case TYPE_SCALAR:
242       {
243         this->_compute_error<Real>(sys_name,
244                                    unknown_name,
245                                    error_vals);
246         break;
247       }
248     case TYPE_VECTOR:
249       {
250         this->_compute_error<RealGradient>(sys_name,
251                                            unknown_name,
252                                            error_vals);
253         break;
254       }
255     default:
256       libmesh_error_msg("Invalid variable type!");
257     }
258 }
259 
260 
261 
262 
263 
error_norm(const std::string & sys_name,const std::string & unknown_name,const FEMNormType & norm)264 Real ExactSolution::error_norm(const std::string & sys_name,
265                                const std::string & unknown_name,
266                                const FEMNormType & norm)
267 {
268   // Check the inputs for validity, and get a reference
269   // to the proper location to store the error
270   std::vector<Real> & error_vals = this->_check_inputs(sys_name,
271                                                        unknown_name);
272 
273   libmesh_assert(_equation_systems.has_system(sys_name));
274   libmesh_assert(_equation_systems.get_system(sys_name).has_variable( unknown_name ));
275   const FEType & fe_type = _equation_systems.get_system(sys_name).variable_type(unknown_name);
276 
277   switch (norm)
278     {
279     case L2:
280       return std::sqrt(error_vals[0]);
281     case H1:
282       return std::sqrt(error_vals[0] + error_vals[1]);
283     case H2:
284       return std::sqrt(error_vals[0] + error_vals[1] + error_vals[2]);
285     case HCURL:
286       {
287         libmesh_error_msg_if(FEInterface::field_type(fe_type) == TYPE_SCALAR,
288                              "Cannot compute HCurl error norm of scalar-valued variables!");
289 
290         return std::sqrt(error_vals[0] + error_vals[5]);
291       }
292     case HDIV:
293       {
294         libmesh_error_msg_if(FEInterface::field_type(fe_type) == TYPE_SCALAR,
295                              "Cannot compute HDiv error norm of scalar-valued variables!");
296 
297         return std::sqrt(error_vals[0] + error_vals[6]);
298       }
299     case H1_SEMINORM:
300       return std::sqrt(error_vals[1]);
301     case H2_SEMINORM:
302       return std::sqrt(error_vals[2]);
303     case HCURL_SEMINORM:
304       {
305         libmesh_error_msg_if(FEInterface::field_type(fe_type) == TYPE_SCALAR,
306                              "Cannot compute HCurl error seminorm of scalar-valued variables!");
307 
308         return std::sqrt(error_vals[5]);
309       }
310     case HDIV_SEMINORM:
311       {
312         libmesh_error_msg_if(FEInterface::field_type(fe_type) == TYPE_SCALAR,
313                              "Cannot compute HDiv error seminorm of scalar-valued variables!");
314 
315         return std::sqrt(error_vals[6]);
316       }
317     case L1:
318       return error_vals[3];
319     case L_INF:
320       return error_vals[4];
321 
322     default:
323       libmesh_error_msg("Currently only Sobolev norms/seminorms are supported!");
324     }
325 }
326 
327 
328 
329 
330 
331 
332 
l2_error(const std::string & sys_name,const std::string & unknown_name)333 Real ExactSolution::l2_error(const std::string & sys_name,
334                              const std::string & unknown_name)
335 {
336 
337   // Check the inputs for validity, and get a reference
338   // to the proper location to store the error
339   std::vector<Real> & error_vals = this->_check_inputs(sys_name,
340                                                        unknown_name);
341 
342   // Return the square root of the first component of the
343   // computed error.
344   return std::sqrt(error_vals[0]);
345 }
346 
347 
348 
349 
350 
351 
352 
l1_error(const std::string & sys_name,const std::string & unknown_name)353 Real ExactSolution::l1_error(const std::string & sys_name,
354                              const std::string & unknown_name)
355 {
356 
357   // Check the inputs for validity, and get a reference
358   // to the proper location to store the error
359   std::vector<Real> & error_vals = this->_check_inputs(sys_name,
360                                                        unknown_name);
361 
362   // Return the square root of the first component of the
363   // computed error.
364   return error_vals[3];
365 }
366 
367 
368 
369 
370 
371 
372 
l_inf_error(const std::string & sys_name,const std::string & unknown_name)373 Real ExactSolution::l_inf_error(const std::string & sys_name,
374                                 const std::string & unknown_name)
375 {
376 
377   // Check the inputs for validity, and get a reference
378   // to the proper location to store the error
379   std::vector<Real> & error_vals = this->_check_inputs(sys_name,
380                                                        unknown_name);
381 
382   // Return the square root of the first component of the
383   // computed error.
384   return error_vals[4];
385 }
386 
387 
388 
389 
390 
391 
392 
h1_error(const std::string & sys_name,const std::string & unknown_name)393 Real ExactSolution::h1_error(const std::string & sys_name,
394                              const std::string & unknown_name)
395 {
396   // If the user has supplied no exact derivative function, we
397   // just integrate the H1 norm of the solution; i.e. its
398   // difference from an "exact solution" of zero.
399 
400   // Check the inputs for validity, and get a reference
401   // to the proper location to store the error
402   std::vector<Real> & error_vals = this->_check_inputs(sys_name,
403                                                        unknown_name);
404 
405   // Return the square root of the sum of the computed errors.
406   return std::sqrt(error_vals[0] + error_vals[1]);
407 }
408 
409 
hcurl_error(const std::string & sys_name,const std::string & unknown_name)410 Real ExactSolution::hcurl_error(const std::string & sys_name,
411                                 const std::string & unknown_name)
412 {
413   return this->error_norm(sys_name,unknown_name,HCURL);
414 }
415 
416 
hdiv_error(const std::string & sys_name,const std::string & unknown_name)417 Real ExactSolution::hdiv_error(const std::string & sys_name,
418                                const std::string & unknown_name)
419 {
420   return this->error_norm(sys_name,unknown_name,HDIV);
421 }
422 
423 
424 
h2_error(const std::string & sys_name,const std::string & unknown_name)425 Real ExactSolution::h2_error(const std::string & sys_name,
426                              const std::string & unknown_name)
427 {
428   // If the user has supplied no exact derivative functions, we
429   // just integrate the H2 norm of the solution; i.e. its
430   // difference from an "exact solution" of zero.
431 
432   // Check the inputs for validity, and get a reference
433   // to the proper location to store the error
434   std::vector<Real> & error_vals = this->_check_inputs(sys_name,
435                                                        unknown_name);
436 
437   // Return the square root of the sum of the computed errors.
438   return std::sqrt(error_vals[0] + error_vals[1] + error_vals[2]);
439 }
440 
441 
442 
443 
444 
445 
446 
447 template<typename OutputShape>
_compute_error(const std::string & sys_name,const std::string & unknown_name,std::vector<Real> & error_vals)448 void ExactSolution::_compute_error(const std::string & sys_name,
449                                    const std::string & unknown_name,
450                                    std::vector<Real> & error_vals)
451 {
452   // Make sure we aren't "overconfigured"
453   libmesh_assert (!(_exact_values.size() && _equation_systems_fine));
454 
455   // We need a communicator.
456   const Parallel::Communicator & communicator(_equation_systems.comm());
457 
458   // This function must be run on all processors at once
459   libmesh_parallel_only(communicator);
460 
461   // Get a reference to the system whose error is being computed.
462   // If we have a fine grid, however, we'll integrate on that instead
463   // for more accuracy.
464   const System & computed_system = _equation_systems_fine ?
465     _equation_systems_fine->get_system(sys_name) :
466     _equation_systems.get_system (sys_name);
467 
468   const Real time = _equation_systems.get_system(sys_name).time;
469 
470   const unsigned int sys_num = computed_system.number();
471   const unsigned int var = computed_system.variable_number(unknown_name);
472   const unsigned int var_component =
473     computed_system.variable_scalar_number(var, 0);
474 
475   // Prepare a global solution and a MeshFunction of the coarse system if we need one
476   std::unique_ptr<MeshFunction> coarse_values;
477   std::unique_ptr<NumericVector<Number>> comparison_soln = NumericVector<Number>::build(_equation_systems.comm());
478   if (_equation_systems_fine)
479     {
480       const System & comparison_system
481         = _equation_systems.get_system(sys_name);
482 
483       std::vector<Number> global_soln;
484       comparison_system.update_global_solution(global_soln);
485       comparison_soln->init(comparison_system.solution->size(), true, SERIAL);
486       (*comparison_soln) = global_soln;
487 
488       coarse_values = libmesh_make_unique<MeshFunction>
489         (_equation_systems,
490          *comparison_soln,
491          comparison_system.get_dof_map(),
492          comparison_system.variable_number(unknown_name));
493       coarse_values->init();
494     }
495 
496   // Initialize any functors we're going to use
497   for (auto & ev : _exact_values)
498     if (ev)
499       ev->init();
500 
501   for (auto & ed : _exact_derivs)
502     if (ed)
503       ed->init();
504 
505   for (auto & eh : _exact_hessians)
506     if (eh)
507       eh->init();
508 
509   // Get a reference to the dofmap and mesh for that system
510   const DofMap & computed_dof_map = computed_system.get_dof_map();
511 
512   const MeshBase & mesh = computed_system.get_mesh();
513 
514   // Grab which element dimensions are present in the mesh
515   const std::set<unsigned char> & elem_dims = mesh.elem_dimensions();
516 
517   // Zero the error before summation
518   // 0 - sum of square of function error (L2)
519   // 1 - sum of square of gradient error (H1 semi)
520   // 2 - sum of square of Hessian error (H2 semi)
521   // 3 - sum of sqrt(square of function error) (L1)
522   // 4 - max of sqrt(square of function error) (Linfty)
523   // 5 - sum of square of curl error (HCurl semi)
524   // 6 - sum of square of div error (HDiv semi)
525   error_vals = std::vector<Real>(7, 0.);
526 
527   // Construct Quadrature rule based on default quadrature order
528   const FEType & fe_type  = computed_dof_map.variable_type(var);
529 
530   unsigned int n_vec_dim = FEInterface::n_vec_dim( mesh, fe_type );
531 
532   // FIXME: MeshFunction needs to be updated to support vector-valued
533   //        elements before we can use a reference solution.
534   if ((n_vec_dim > 1) && _equation_systems_fine)
535     {
536       libMesh::err << "Error calculation using reference solution not yet\n"
537                    << "supported for vector-valued elements."
538                    << std::endl;
539       libmesh_not_implemented();
540     }
541 
542 
543   // Allow space for dims 0-3, even if we don't use them all
544   std::vector<std::unique_ptr<FEGenericBase<OutputShape>>> fe_ptrs(4);
545   std::vector<std::unique_ptr<QBase>> q_rules(4);
546 
547   // Prepare finite elements for each dimension present in the mesh
548   for (const auto dim : elem_dims)
549     {
550       // Build a quadrature rule.
551       q_rules[dim] = fe_type.default_quadrature_rule (dim, _extra_order);
552 
553       // Construct finite element object
554       fe_ptrs[dim] = FEGenericBase<OutputShape>::build(dim, fe_type);
555 
556       // Attach quadrature rule to FE object
557       fe_ptrs[dim]->attach_quadrature_rule (q_rules[dim].get());
558     }
559 
560   // The global degree of freedom indices associated
561   // with the local degrees of freedom.
562   std::vector<dof_id_type> dof_indices;
563 
564 
565   //
566   // Begin the loop over the elements
567   //
568   // TODO: this ought to be threaded (and using subordinate
569   // MeshFunction objects in each thread rather than a single
570   // master)
571   for (const auto & elem : mesh.active_local_element_ptr_range())
572     {
573       // Skip this element if it is in a subdomain excluded by the user.
574       const subdomain_id_type elem_subid = elem->subdomain_id();
575       if (_excluded_subdomains.count(elem_subid))
576         continue;
577 
578       // The spatial dimension of the current Elem. FEs and other data
579       // are indexed on dim.
580       const unsigned int dim = elem->dim();
581 
582       // If the variable is not active on this subdomain, don't bother
583       if (!computed_system.variable(var).active_on_subdomain(elem_subid))
584         continue;
585 
586       /* If the variable is active, then we're going to restrict the
587          MeshFunction evaluations to the current element subdomain.
588          This is for cases such as mixed dimension meshes where we want
589          to restrict the calculation to one particular domain. */
590       std::set<subdomain_id_type> subdomain_id;
591       subdomain_id.insert(elem_subid);
592 
593       FEGenericBase<OutputShape> * fe = fe_ptrs[dim].get();
594       QBase * qrule = q_rules[dim].get();
595       libmesh_assert(fe);
596       libmesh_assert(qrule);
597 
598       // The Jacobian*weight at the quadrature points.
599       const std::vector<Real> & JxW = fe->get_JxW();
600 
601       // The value of the shape functions at the quadrature points
602       // i.e. phi(i) = phi_values[i][qp]
603       const std::vector<std::vector<OutputShape>> &  phi_values = fe->get_phi();
604 
605       // The value of the shape function gradients at the quadrature points
606       const std::vector<std::vector<typename FEGenericBase<OutputShape>::OutputGradient>> &
607         dphi_values = fe->get_dphi();
608 
609       // The value of the shape function curls at the quadrature points
610       // Only computed for vector-valued elements
611       const std::vector<std::vector<typename FEGenericBase<OutputShape>::OutputShape>> * curl_values = nullptr;
612 
613       // The value of the shape function divergences at the quadrature points
614       // Only computed for vector-valued elements
615       const std::vector<std::vector<typename FEGenericBase<OutputShape>::OutputDivergence>> * div_values = nullptr;
616 
617       if (FEInterface::field_type(fe_type) == TYPE_VECTOR)
618         {
619           curl_values = &fe->get_curl_phi();
620           div_values = &fe->get_div_phi();
621         }
622 
623 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
624       // The value of the shape function second derivatives at the quadrature points
625       const std::vector<std::vector<typename FEGenericBase<OutputShape>::OutputTensor>> &
626         d2phi_values = fe->get_d2phi();
627 #endif
628 
629       // The XYZ locations (in physical space) of the quadrature points
630       const std::vector<Point> & q_point = fe->get_xyz();
631 
632       // reinitialize the element-specific data
633       // for the current element
634       fe->reinit (elem);
635 
636       // Get the local to global degree of freedom maps
637       computed_dof_map.dof_indices    (elem, dof_indices, var);
638 
639       // The number of quadrature points
640       const unsigned int n_qp = qrule->n_points();
641 
642       // The number of shape functions
643       const unsigned int n_sf =
644         cast_int<unsigned int>(dof_indices.size());
645 
646       //
647       // Begin the loop over the Quadrature points.
648       //
649       for (unsigned int qp=0; qp<n_qp; qp++)
650         {
651           // Real u_h = 0.;
652           // RealGradient grad_u_h;
653 
654           typename FEGenericBase<OutputShape>::OutputNumber u_h(0.);
655 
656           typename FEGenericBase<OutputShape>::OutputNumberGradient grad_u_h;
657 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
658           typename FEGenericBase<OutputShape>::OutputNumberTensor grad2_u_h;
659 #endif
660           typename FEGenericBase<OutputShape>::OutputNumber curl_u_h(0.0);
661           typename FEGenericBase<OutputShape>::OutputNumberDivergence div_u_h = 0.0;
662 
663           // Compute solution values at the current
664           // quadrature point.  This requires a sum
665           // over all the shape functions evaluated
666           // at the quadrature point.
667           for (unsigned int i=0; i<n_sf; i++)
668             {
669               // Values from current solution.
670               u_h      += phi_values[i][qp]*computed_system.current_solution  (dof_indices[i]);
671               grad_u_h += dphi_values[i][qp]*computed_system.current_solution (dof_indices[i]);
672 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
673               grad2_u_h += d2phi_values[i][qp]*computed_system.current_solution (dof_indices[i]);
674 #endif
675               if (FEInterface::field_type(fe_type) == TYPE_VECTOR)
676                 {
677                   curl_u_h += (*curl_values)[i][qp]*computed_system.current_solution (dof_indices[i]);
678                   div_u_h += (*div_values)[i][qp]*computed_system.current_solution (dof_indices[i]);
679                 }
680             }
681 
682           // Compute the value of the error at this quadrature point
683           typename FEGenericBase<OutputShape>::OutputNumber exact_val(0);
684           RawAccessor<typename FEGenericBase<OutputShape>::OutputNumber> exact_val_accessor( exact_val, dim );
685           if (_exact_values.size() > sys_num && _exact_values[sys_num])
686             {
687               for (unsigned int c = 0; c < n_vec_dim; c++)
688                 exact_val_accessor(c) =
689                   _exact_values[sys_num]->
690                   component(var_component+c, q_point[qp], time);
691             }
692           else if (_equation_systems_fine)
693             {
694               // FIXME: Needs to be updated for vector-valued elements
695               DenseVector<Number> output(1);
696               (*coarse_values)(q_point[qp],time,output,&subdomain_id);
697               exact_val = output(0);
698             }
699           const typename FEGenericBase<OutputShape>::OutputNumber val_error = u_h - exact_val;
700 
701           // Add the squares of the error to each contribution
702           Real error_sq = TensorTools::norm_sq(val_error);
703           error_vals[0] += JxW[qp]*error_sq;
704 
705           Real norm = sqrt(error_sq);
706           error_vals[3] += JxW[qp]*norm;
707 
708           if (error_vals[4]<norm) { error_vals[4] = norm; }
709 
710           // Compute the value of the error in the gradient at this
711           // quadrature point
712           typename FEGenericBase<OutputShape>::OutputNumberGradient exact_grad;
713           RawAccessor<typename FEGenericBase<OutputShape>::OutputNumberGradient> exact_grad_accessor( exact_grad, LIBMESH_DIM );
714           if (_exact_derivs.size() > sys_num && _exact_derivs[sys_num])
715             {
716               for (unsigned int c = 0; c < n_vec_dim; c++)
717                 for (unsigned int d = 0; d < LIBMESH_DIM; d++)
718                   exact_grad_accessor(d + c*LIBMESH_DIM) =
719                     _exact_derivs[sys_num]->
720                     component(var_component+c, q_point[qp], time)(d);
721             }
722           else if (_equation_systems_fine)
723             {
724               // FIXME: Needs to be updated for vector-valued elements
725               std::vector<Gradient> output(1);
726               coarse_values->gradient(q_point[qp],time,output,&subdomain_id);
727               exact_grad = output[0];
728             }
729 
730           const typename FEGenericBase<OutputShape>::OutputNumberGradient grad_error = grad_u_h - exact_grad;
731 
732           error_vals[1] += JxW[qp]*grad_error.norm_sq();
733 
734 
735           if (FEInterface::field_type(fe_type) == TYPE_VECTOR)
736             {
737               // Compute the value of the error in the curl at this
738               // quadrature point
739               typename FEGenericBase<OutputShape>::OutputNumber exact_curl(0.0);
740               if (_exact_derivs.size() > sys_num && _exact_derivs[sys_num])
741                 {
742                   exact_curl = TensorTools::curl_from_grad( exact_grad );
743                 }
744               else if (_equation_systems_fine)
745                 {
746                   // FIXME: Need to implement curl for MeshFunction and support reference
747                   //        solution for vector-valued elements
748                 }
749 
750               const typename FEGenericBase<OutputShape>::OutputNumber curl_error = curl_u_h - exact_curl;
751 
752               error_vals[5] += JxW[qp]*TensorTools::norm_sq(curl_error);
753 
754               // Compute the value of the error in the divergence at this
755               // quadrature point
756               typename FEGenericBase<OutputShape>::OutputNumberDivergence exact_div = 0.0;
757               if (_exact_derivs.size() > sys_num && _exact_derivs[sys_num])
758                 {
759                   exact_div = TensorTools::div_from_grad( exact_grad );
760                 }
761               else if (_equation_systems_fine)
762                 {
763                   // FIXME: Need to implement div for MeshFunction and support reference
764                   //        solution for vector-valued elements
765                 }
766 
767               const typename FEGenericBase<OutputShape>::OutputNumberDivergence div_error = div_u_h - exact_div;
768 
769               error_vals[6] += JxW[qp]*TensorTools::norm_sq(div_error);
770             }
771 
772 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
773           // Compute the value of the error in the hessian at this
774           // quadrature point
775           typename FEGenericBase<OutputShape>::OutputNumberTensor exact_hess;
776           RawAccessor<typename FEGenericBase<OutputShape>::OutputNumberTensor> exact_hess_accessor( exact_hess, dim );
777           if (_exact_hessians.size() > sys_num && _exact_hessians[sys_num])
778             {
779               //FIXME: This needs to be implemented to support rank 3 tensors
780               //       which can't happen until type_n_tensor is fully implemented
781               //       and a RawAccessor<TypeNTensor> is fully implemented
782               if (FEInterface::field_type(fe_type) == TYPE_VECTOR)
783                 libmesh_not_implemented();
784 
785               for (unsigned int c = 0; c < n_vec_dim; c++)
786                 for (unsigned int d = 0; d < dim; d++)
787                   for (unsigned int e =0; e < dim; e++)
788                     exact_hess_accessor(d + e*dim + c*dim*dim) =
789                       _exact_hessians[sys_num]->
790                       component(var_component+c, q_point[qp], time)(d,e);
791             }
792           else if (_equation_systems_fine)
793             {
794               // FIXME: Needs to be updated for vector-valued elements
795               std::vector<Tensor> output(1);
796               coarse_values->hessian(q_point[qp],time,output,&subdomain_id);
797               exact_hess = output[0];
798             }
799 
800           const typename FEGenericBase<OutputShape>::OutputNumberTensor grad2_error = grad2_u_h - exact_hess;
801 
802           // FIXME: PB: Is this what we want for rank 3 tensors?
803           error_vals[2] += JxW[qp]*grad2_error.norm_sq();
804 #endif
805 
806         } // end qp loop
807     } // end element loop
808 
809   // Add up the error values on all processors, except for the L-infty
810   // norm, for which the maximum is computed.
811   Real l_infty_norm = error_vals[4];
812   communicator.max(l_infty_norm);
813   communicator.sum(error_vals);
814   error_vals[4] = l_infty_norm;
815 }
816 
817 // Explicit instantiations of templated member functions
818 template void ExactSolution::_compute_error<Real>(const std::string &, const std::string &, std::vector<Real> &);
819 template void ExactSolution::_compute_error<RealGradient>(const std::string &, const std::string &, std::vector<Real> &);
820 
821 } // namespace libMesh
822