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