Line data Source code
1 : // The libMesh Finite Element Library.
2 : // Copyright (C) 2002-2025 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/mesh_serializer.h"
28 : #include "libmesh/numeric_vector.h"
29 : #include "libmesh/parallel.h"
30 : #include "libmesh/quadrature.h"
31 : #include "libmesh/wrapped_function.h"
32 : #include "libmesh/wrapped_functor.h"
33 : #include "libmesh/fe_interface.h"
34 : #include "libmesh/raw_accessor.h"
35 : #include "libmesh/tensor_tools.h"
36 : #include "libmesh/enum_norm_type.h"
37 : #include "libmesh/utility.h"
38 :
39 : // C++ Includes
40 : #include <memory>
41 :
42 :
43 : namespace libMesh
44 : {
45 :
46 7167 : ExactSolution::ExactSolution(const EquationSystems & es) :
47 6763 : _equation_systems(es),
48 6763 : _equation_systems_fine(nullptr),
49 7975 : _extra_order(1)
50 : {
51 : // Initialize the _errors data structure which holds all
52 : // the eventual values of the error.
53 15610 : for (auto sys : make_range(_equation_systems.n_systems()))
54 : {
55 : // Reference to the system
56 8443 : const System & system = _equation_systems.get_system(sys);
57 :
58 : // The name of the system
59 238 : const std::string & sys_name = system.name();
60 :
61 : // The SystemErrorMap to be inserted
62 476 : ExactSolution::SystemErrorMap sem;
63 :
64 22968 : for (auto var : make_range(system.n_vars()))
65 : {
66 : // The name of this variable
67 410 : const std::string & var_name = system.variable_name(var);
68 28640 : sem[var_name] = std::vector<Real>(5, 0.);
69 : }
70 :
71 8443 : _errors[sys_name] = sem;
72 : }
73 7167 : }
74 :
75 :
76 0 : ExactSolution::ExactSolution(ExactSolution &&) = default;
77 13526 : ExactSolution::~ExactSolution() = default;
78 :
79 :
80 0 : void ExactSolution::
81 : set_excluded_subdomains(const std::set<subdomain_id_type> & excluded)
82 : {
83 0 : _excluded_subdomains = excluded;
84 0 : }
85 :
86 71 : void ExactSolution::attach_reference_solution (const EquationSystems * es_fine)
87 : {
88 2 : libmesh_assert(es_fine);
89 71 : _equation_systems_fine = es_fine;
90 :
91 : // If we're using a fine grid solution, we're not using exact values
92 69 : _exact_values.clear();
93 69 : _exact_derivs.clear();
94 69 : _exact_hessians.clear();
95 71 : }
96 :
97 :
98 2339 : void ExactSolution::attach_exact_value (ValueFunctionPointer fptr)
99 : {
100 66 : libmesh_assert(fptr);
101 :
102 : // Clear out any previous _exact_values entries, then add a new
103 : // entry for each system.
104 2339 : _exact_values.clear();
105 :
106 4818 : for (auto sys : make_range(_equation_systems.n_systems()))
107 : {
108 2479 : const System & system = _equation_systems.get_system(sys);
109 2549 : _exact_values.emplace_back(std::make_unique<WrappedFunctor<Number>>(system, fptr, &_equation_systems.parameters));
110 : }
111 :
112 : // If we're using exact values, we're not using a fine grid solution
113 2339 : _equation_systems_fine = nullptr;
114 2339 : }
115 :
116 :
117 4189 : void ExactSolution::attach_exact_values (const std::vector<FunctionBase<Number> *> & f)
118 : {
119 : // Automatically delete any previous _exact_values entries, then add a new
120 : // entry for each system.
121 4189 : _exact_values.clear();
122 :
123 8378 : for (auto ptr : f)
124 4425 : _exact_values.emplace_back(ptr ? std::make_unique<WrappedFunctor<Number>>(*ptr) : nullptr);
125 4189 : }
126 :
127 :
128 0 : void ExactSolution::attach_exact_values (const std::vector<FEMFunctionBase<Number> *> & f)
129 : {
130 : // Automatically delete any previous _exact_values entries, then add a new
131 : // entry for each system.
132 0 : _exact_values.clear();
133 :
134 0 : for (auto ptr : f)
135 0 : _exact_values.emplace_back(ptr ? ptr->clone() : nullptr);
136 0 : }
137 :
138 :
139 142 : void ExactSolution::attach_exact_value (unsigned int sys_num,
140 : FunctionBase<Number> * f)
141 : {
142 146 : if (_exact_values.size() <= sys_num)
143 142 : _exact_values.resize(sys_num+1);
144 :
145 142 : if (f)
146 146 : _exact_values[sys_num] = std::make_unique<WrappedFunctor<Number>>(*f);
147 142 : }
148 :
149 :
150 0 : void ExactSolution::attach_exact_value (unsigned int sys_num,
151 : FEMFunctionBase<Number> * f)
152 : {
153 0 : if (_exact_values.size() <= sys_num)
154 0 : _exact_values.resize(sys_num+1);
155 :
156 0 : if (f)
157 0 : _exact_values[sys_num] = f->clone();
158 0 : }
159 :
160 :
161 2059 : void ExactSolution::attach_exact_deriv (GradientFunctionPointer gptr)
162 : {
163 58 : libmesh_assert(gptr);
164 :
165 : // Clear out any previous _exact_derivs entries, then add a new
166 : // entry for each system.
167 2059 : _exact_derivs.clear();
168 :
169 4118 : for (auto sys : make_range(_equation_systems.n_systems()))
170 : {
171 2059 : const System & system = _equation_systems.get_system(sys);
172 2117 : _exact_derivs.emplace_back(std::make_unique<WrappedFunctor<Gradient>>(system, gptr, &_equation_systems.parameters));
173 : }
174 :
175 : // If we're using exact values, we're not using a fine grid solution
176 2059 : _equation_systems_fine = nullptr;
177 2059 : }
178 :
179 :
180 4189 : void ExactSolution::attach_exact_derivs (const std::vector<FunctionBase<Gradient> *> & g)
181 : {
182 : // Automatically delete any previous _exact_derivs entries, then add a new
183 : // entry for each system.
184 4189 : _exact_derivs.clear();
185 :
186 8378 : for (auto ptr : g)
187 4425 : _exact_derivs.emplace_back(ptr ? std::make_unique<WrappedFunctor<Gradient>>(*ptr) : nullptr);
188 4189 : }
189 :
190 :
191 0 : void ExactSolution::attach_exact_derivs (const std::vector<FEMFunctionBase<Gradient> *> & g)
192 : {
193 : // Automatically delete any previous _exact_derivs entries, then add a new
194 : // entry for each system.
195 0 : _exact_derivs.clear();
196 :
197 0 : for (auto ptr : g)
198 0 : _exact_derivs.emplace_back(ptr ? ptr->clone() : nullptr);
199 0 : }
200 :
201 :
202 0 : void ExactSolution::attach_exact_deriv (unsigned int sys_num,
203 : FunctionBase<Gradient> * g)
204 : {
205 0 : if (_exact_derivs.size() <= sys_num)
206 0 : _exact_derivs.resize(sys_num+1);
207 :
208 0 : if (g)
209 0 : _exact_derivs[sys_num] = std::make_unique<WrappedFunctor<Gradient>>(*g);
210 0 : }
211 :
212 :
213 0 : void ExactSolution::attach_exact_deriv (unsigned int sys_num,
214 : FEMFunctionBase<Gradient> * g)
215 : {
216 0 : if (_exact_derivs.size() <= sys_num)
217 0 : _exact_derivs.resize(sys_num+1);
218 :
219 0 : if (g)
220 0 : _exact_derivs[sys_num] = g->clone();
221 0 : }
222 :
223 :
224 426 : void ExactSolution::attach_exact_hessian (HessianFunctionPointer hptr)
225 : {
226 12 : libmesh_assert(hptr);
227 :
228 : // Clear out any previous _exact_hessians entries, then add a new
229 : // entry for each system.
230 426 : _exact_hessians.clear();
231 :
232 852 : for (auto sys : make_range(_equation_systems.n_systems()))
233 : {
234 426 : const System & system = _equation_systems.get_system(sys);
235 438 : _exact_hessians.emplace_back(std::make_unique<WrappedFunctor<Tensor>>(system, hptr, &_equation_systems.parameters));
236 : }
237 :
238 : // If we're using exact values, we're not using a fine grid solution
239 426 : _equation_systems_fine = nullptr;
240 426 : }
241 :
242 :
243 0 : void ExactSolution::attach_exact_hessians (std::vector<FunctionBase<Tensor> *> h)
244 : {
245 : // Automatically delete any previous _exact_hessians entries, then add a new
246 : // entry for each system.
247 0 : _exact_hessians.clear();
248 :
249 0 : for (auto ptr : h)
250 0 : _exact_hessians.emplace_back(ptr ? std::make_unique<WrappedFunctor<Tensor>>(*ptr) : nullptr);
251 0 : }
252 :
253 :
254 0 : void ExactSolution::attach_exact_hessians (std::vector<FEMFunctionBase<Tensor> *> h)
255 : {
256 : // Automatically delete any previous _exact_hessians entries, then add a new
257 : // entry for each system.
258 0 : _exact_hessians.clear();
259 :
260 0 : for (auto ptr : h)
261 0 : _exact_hessians.emplace_back(ptr ? ptr->clone() : nullptr);
262 0 : }
263 :
264 :
265 0 : void ExactSolution::attach_exact_hessian (unsigned int sys_num,
266 : FunctionBase<Tensor> * h)
267 : {
268 0 : if (_exact_hessians.size() <= sys_num)
269 0 : _exact_hessians.resize(sys_num+1);
270 :
271 0 : if (h)
272 0 : _exact_hessians[sys_num] = std::make_unique<WrappedFunctor<Tensor>>(*h);
273 0 : }
274 :
275 :
276 0 : void ExactSolution::attach_exact_hessian (unsigned int sys_num,
277 : FEMFunctionBase<Tensor> * h)
278 : {
279 0 : if (_exact_hessians.size() <= sys_num)
280 0 : _exact_hessians.resize(sys_num+1);
281 :
282 0 : if (h)
283 0 : _exact_hessians[sys_num] = h->clone();
284 0 : }
285 :
286 :
287 126421 : std::vector<Real> & ExactSolution::_check_inputs(std::string_view sys_name,
288 : std::string_view unknown_name)
289 : {
290 : // Return a reference to the proper error entry, or throw an error
291 : // if it doesn't exist.
292 126421 : auto & system_error_map = libmesh_map_find(_errors, sys_name);
293 126421 : return libmesh_map_find(system_error_map, unknown_name);
294 : }
295 :
296 :
297 :
298 27249 : void ExactSolution::compute_error(std::string_view sys_name,
299 : std::string_view unknown_name)
300 : {
301 : // Check the inputs for validity, and get a reference
302 : // to the proper location to store the error
303 25713 : std::vector<Real> & error_vals = this->_check_inputs(sys_name,
304 1536 : unknown_name);
305 :
306 768 : libmesh_assert( _equation_systems.has_system(sys_name) );
307 27249 : const System & sys = _equation_systems.get_system<System>( sys_name );
308 :
309 768 : libmesh_assert( sys.has_variable( unknown_name ) );
310 27249 : switch( FEInterface::field_type(sys.variable_type( unknown_name )) )
311 : {
312 22350 : case TYPE_SCALAR:
313 : {
314 22350 : this->_compute_error<Real>(sys_name,
315 : unknown_name,
316 : error_vals);
317 22350 : break;
318 : }
319 4899 : case TYPE_VECTOR:
320 : {
321 4899 : this->_compute_error<RealGradient>(sys_name,
322 : unknown_name,
323 : error_vals);
324 4899 : break;
325 : }
326 0 : default:
327 0 : libmesh_error_msg("Invalid variable type!");
328 : }
329 27249 : }
330 :
331 :
332 :
333 :
334 :
335 6816 : Real ExactSolution::error_norm(std::string_view sys_name,
336 : std::string_view unknown_name,
337 : const FEMNormType & norm)
338 : {
339 : // Check the inputs for validity, and get a reference
340 : // to the proper location to store the error
341 6432 : std::vector<Real> & error_vals = this->_check_inputs(sys_name,
342 384 : unknown_name);
343 :
344 192 : libmesh_assert(_equation_systems.has_system(sys_name));
345 192 : libmesh_assert(_equation_systems.get_system(sys_name).has_variable( unknown_name ));
346 6816 : const FEType & fe_type = _equation_systems.get_system(sys_name).variable_type(unknown_name);
347 :
348 6816 : switch (norm)
349 : {
350 0 : case L2:
351 0 : return std::sqrt(error_vals[0]);
352 0 : case H1:
353 0 : return std::sqrt(error_vals[0] + error_vals[1]);
354 0 : case H2:
355 0 : return std::sqrt(error_vals[0] + error_vals[1] + error_vals[2]);
356 852 : case HCURL:
357 : {
358 852 : libmesh_error_msg_if(FEInterface::field_type(fe_type) == TYPE_SCALAR,
359 : "Cannot compute HCurl error norm of scalar-valued variables!");
360 :
361 852 : return std::sqrt(error_vals[0] + error_vals[5]);
362 : }
363 2556 : case HDIV:
364 : {
365 2556 : libmesh_error_msg_if(FEInterface::field_type(fe_type) == TYPE_SCALAR,
366 : "Cannot compute HDiv error norm of scalar-valued variables!");
367 :
368 2556 : return std::sqrt(error_vals[0] + error_vals[6]);
369 : }
370 0 : case H1_SEMINORM:
371 0 : return std::sqrt(error_vals[1]);
372 0 : case H2_SEMINORM:
373 0 : return std::sqrt(error_vals[2]);
374 852 : case HCURL_SEMINORM:
375 : {
376 852 : libmesh_error_msg_if(FEInterface::field_type(fe_type) == TYPE_SCALAR,
377 : "Cannot compute HCurl error seminorm of scalar-valued variables!");
378 :
379 852 : return std::sqrt(error_vals[5]);
380 : }
381 2556 : case HDIV_SEMINORM:
382 : {
383 2556 : libmesh_error_msg_if(FEInterface::field_type(fe_type) == TYPE_SCALAR,
384 : "Cannot compute HDiv error seminorm of scalar-valued variables!");
385 :
386 2556 : return std::sqrt(error_vals[6]);
387 : }
388 0 : case L1:
389 0 : return error_vals[3];
390 0 : case L_INF:
391 0 : return error_vals[4];
392 :
393 0 : default:
394 0 : libmesh_error_msg("Currently only Sobolev norms/seminorms are supported!");
395 : }
396 : }
397 :
398 :
399 :
400 :
401 :
402 :
403 :
404 48833 : Real ExactSolution::l2_error(std::string_view sys_name,
405 : std::string_view unknown_name)
406 : {
407 :
408 : // Check the inputs for validity, and get a reference
409 : // to the proper location to store the error
410 46081 : std::vector<Real> & error_vals = this->_check_inputs(sys_name,
411 2752 : unknown_name);
412 :
413 : // Return the square root of the first component of the
414 : // computed error.
415 48833 : return std::sqrt(error_vals[0]);
416 : }
417 :
418 :
419 :
420 :
421 :
422 :
423 :
424 0 : Real ExactSolution::l1_error(std::string_view sys_name,
425 : std::string_view unknown_name)
426 : {
427 :
428 : // Check the inputs for validity, and get a reference
429 : // to the proper location to store the error
430 0 : std::vector<Real> & error_vals = this->_check_inputs(sys_name,
431 0 : unknown_name);
432 :
433 : // Return the square root of the first component of the
434 : // computed error.
435 0 : return error_vals[3];
436 : }
437 :
438 :
439 :
440 :
441 :
442 :
443 :
444 0 : Real ExactSolution::l_inf_error(std::string_view sys_name,
445 : std::string_view unknown_name)
446 : {
447 :
448 : // Check the inputs for validity, and get a reference
449 : // to the proper location to store the error
450 0 : std::vector<Real> & error_vals = this->_check_inputs(sys_name,
451 0 : unknown_name);
452 :
453 : // Return the square root of the first component of the
454 : // computed error.
455 0 : return error_vals[4];
456 : }
457 :
458 :
459 :
460 :
461 :
462 :
463 :
464 29891 : Real ExactSolution::h1_error(std::string_view sys_name,
465 : std::string_view unknown_name)
466 : {
467 : // If the user has supplied no exact derivative function, we
468 : // just integrate the H1 norm of the solution; i.e. its
469 : // difference from an "exact solution" of zero.
470 :
471 : // Check the inputs for validity, and get a reference
472 : // to the proper location to store the error
473 28207 : std::vector<Real> & error_vals = this->_check_inputs(sys_name,
474 1684 : unknown_name);
475 :
476 : // Return the square root of the sum of the computed errors.
477 29891 : return std::sqrt(error_vals[0] + error_vals[1]);
478 : }
479 :
480 :
481 852 : Real ExactSolution::hcurl_error(std::string_view sys_name,
482 : std::string_view unknown_name)
483 : {
484 852 : return this->error_norm(sys_name,unknown_name,HCURL);
485 : }
486 :
487 :
488 2556 : Real ExactSolution::hdiv_error(std::string_view sys_name,
489 : std::string_view unknown_name)
490 : {
491 2556 : return this->error_norm(sys_name,unknown_name,HDIV);
492 : }
493 :
494 :
495 :
496 13632 : Real ExactSolution::h2_error(std::string_view sys_name,
497 : std::string_view unknown_name)
498 : {
499 : // If the user has supplied no exact derivative functions, we
500 : // just integrate the H2 norm of the solution; i.e. its
501 : // difference from an "exact solution" of zero.
502 :
503 : // Check the inputs for validity, and get a reference
504 : // to the proper location to store the error
505 12864 : std::vector<Real> & error_vals = this->_check_inputs(sys_name,
506 768 : unknown_name);
507 :
508 : // Return the square root of the sum of the computed errors.
509 13632 : return std::sqrt(error_vals[0] + error_vals[1] + error_vals[2]);
510 : }
511 :
512 :
513 :
514 :
515 :
516 :
517 :
518 : template<typename OutputShape>
519 27249 : void ExactSolution::_compute_error(std::string_view sys_name,
520 : std::string_view unknown_name,
521 : std::vector<Real> & error_vals)
522 : {
523 : // Make sure we aren't "overconfigured"
524 768 : libmesh_assert (!(_exact_values.size() && _equation_systems_fine));
525 :
526 : // We need a communicator.
527 27249 : const Parallel::Communicator & communicator(_equation_systems.comm());
528 :
529 : // This function must be run on all processors at once
530 768 : libmesh_parallel_only(communicator);
531 :
532 : // Get a reference to the system whose error is being computed.
533 : // If we have a fine grid, however, we'll integrate on that instead
534 : // for more accuracy.
535 27249 : const System & computed_system = _equation_systems_fine ?
536 71 : _equation_systems_fine->get_system(sys_name) :
537 27178 : _equation_systems.get_system (sys_name);
538 :
539 28785 : FEMContext context(computed_system);
540 :
541 1536 : const MeshBase & mesh = computed_system.get_mesh();
542 :
543 27249 : const Real time = _equation_systems.get_system(sys_name).time;
544 :
545 1536 : const unsigned int sys_num = computed_system.number();
546 27249 : const unsigned int var = computed_system.variable_number(unknown_name);
547 768 : unsigned int var_component = 0;
548 37281 : for (const auto var_num : make_range(var))
549 : {
550 284 : const auto & var_fe_type = computed_system.variable_type(var_num);
551 10032 : const auto var_vec_dim = FEInterface::n_vec_dim(mesh, var_fe_type);
552 10032 : var_component += var_vec_dim;
553 : }
554 :
555 : // Prepare a global solution, a serialized mesh, and a MeshFunction
556 : // of the coarse system, if we need them for fine-system integration
557 26481 : std::unique_ptr<MeshFunction> coarse_values;
558 28785 : std::unique_ptr<NumericVector<Number>> comparison_soln =
559 27249 : NumericVector<Number>::build(_equation_systems.comm());
560 : MeshSerializer
561 28017 : serial(const_cast<MeshBase&>(_equation_systems.get_mesh()),
562 27249 : _equation_systems_fine);
563 27249 : if (_equation_systems_fine)
564 : {
565 : const System & comparison_system
566 71 : = _equation_systems.get_system(sys_name);
567 :
568 4 : std::vector<Number> global_soln;
569 71 : comparison_system.update_global_solution(global_soln);
570 73 : comparison_soln->init(comparison_system.solution->size(), true, SERIAL);
571 71 : (*comparison_soln) = global_soln;
572 :
573 138 : coarse_values = std::make_unique<MeshFunction>
574 : (_equation_systems,
575 2 : *comparison_soln,
576 : comparison_system.get_dof_map(),
577 138 : comparison_system.variable_number(unknown_name));
578 71 : coarse_values->init();
579 : }
580 :
581 : // Grab which element dimensions are present in the mesh
582 768 : const std::set<unsigned char> & elem_dims = mesh.elem_dimensions();
583 :
584 : // Initialize any functors we're going to use
585 51439 : for (auto & ev : _exact_values)
586 24190 : if (ev)
587 : {
588 24190 : ev->init();
589 24190 : ev->init_context(context);
590 : }
591 :
592 50037 : for (auto & ed : _exact_derivs)
593 22788 : if (ed)
594 : {
595 22788 : ed->init();
596 22788 : ed->init_context(context);
597 : }
598 :
599 30657 : for (auto & eh : _exact_hessians)
600 3408 : if (eh)
601 : {
602 3408 : eh->init();
603 3408 : eh->init_context(context);
604 : }
605 :
606 : // If we have *no* functors we intend to use (because we're using a
607 : // fine system, or because our exact solution is zero and we're just
608 : // computing norms in an outdated way) then let our FE objects know
609 : // we don't actually need anything from them, so they don't think
610 : // we've just invoked them in a deprecated "compute everything"
611 : // fashion.
612 27347 : if (_exact_values.empty() && _exact_derivs.empty() &&
613 98 : _exact_hessians.empty())
614 6958 : for (auto dim : elem_dims)
615 6958 : for (auto v : make_range(computed_system.n_vars()))
616 : {
617 : FEAbstract * fe;
618 98 : context.get_element_fe(v, fe, dim);
619 98 : fe->get_nothing();
620 : }
621 :
622 : // Get a reference to the dofmap and mesh for that system
623 768 : const DofMap & computed_dof_map = computed_system.get_dof_map();
624 :
625 : // Zero the error before summation
626 : // 0 - sum of square of function error (L2)
627 : // 1 - sum of square of gradient error (H1 semi)
628 : // 2 - sum of square of Hessian error (H2 semi)
629 : // 3 - sum of sqrt(square of function error) (L1)
630 : // 4 - max of sqrt(square of function error) (Linfty)
631 : // 5 - sum of square of curl error (HCurl semi)
632 : // 6 - sum of square of div error (HDiv semi)
633 53730 : error_vals = std::vector<Real>(7, 0.);
634 :
635 : // Construct Quadrature rule based on default quadrature order
636 768 : const FEType & fe_type = computed_dof_map.variable_type(var);
637 27249 : const auto field_type = FEInterface::field_type(fe_type);
638 :
639 27249 : unsigned int n_vec_dim = FEInterface::n_vec_dim( mesh, fe_type );
640 :
641 : // FIXME: MeshFunction needs to be updated to support vector-valued
642 : // elements before we can use a reference solution.
643 27249 : if ((n_vec_dim > 1) && _equation_systems_fine)
644 : {
645 0 : libMesh::err << "Error calculation using reference solution not yet\n"
646 0 : << "supported for vector-valued elements."
647 0 : << std::endl;
648 0 : libmesh_not_implemented();
649 : }
650 :
651 :
652 : // Allow space for dims 0-3, even if we don't use them all
653 28785 : std::vector<std::unique_ptr<FEGenericBase<OutputShape>>> fe_ptrs(4);
654 28785 : std::vector<std::unique_ptr<QBase>> q_rules(4);
655 :
656 : // Prepare finite elements for each dimension present in the mesh
657 54569 : for (const auto dim : elem_dims)
658 : {
659 : // Build a quadrature rule.
660 27320 : q_rules[dim] = fe_type.default_quadrature_rule (dim, _extra_order);
661 :
662 : // Disallow rules with negative weights. That will use more
663 : // quadrature points, but we're going to be taking square roots
664 : // of element integral results here!
665 27320 : q_rules[dim]->allow_rules_with_negative_weights = false;
666 :
667 : // Construct finite element object
668 53100 : fe_ptrs[dim] = FEGenericBase<OutputShape>::build(dim, fe_type);
669 :
670 : // Attach quadrature rule to FE object
671 28860 : fe_ptrs[dim]->attach_quadrature_rule (q_rules[dim].get());
672 : }
673 :
674 : // The global degree of freedom indices associated
675 : // with the local degrees of freedom.
676 768 : std::vector<dof_id_type> dof_indices;
677 :
678 :
679 : //
680 : // Begin the loop over the elements
681 : //
682 : // TODO: this ought to be threaded (and using subordinate
683 : // MeshFunction objects in each thread rather than a single
684 : // master)
685 3117258 : for (const auto & elem : mesh.active_local_element_ptr_range())
686 : {
687 : // Skip this element if it is in a subdomain excluded by the user.
688 1657028 : const subdomain_id_type elem_subid = elem->subdomain_id();
689 138120 : if (_excluded_subdomains.count(elem_subid))
690 0 : continue;
691 :
692 : // The spatial dimension of the current Elem. FEs and other data
693 : // are indexed on dim.
694 1657028 : const unsigned int dim = elem->dim();
695 :
696 : // If the variable is not active on this subdomain, don't bother
697 1657028 : if (!computed_system.variable(var).active_on_subdomain(elem_subid))
698 0 : continue;
699 :
700 : /* If the variable is active, then we're going to restrict the
701 : MeshFunction evaluations to the current element subdomain.
702 : This is for cases such as mixed dimension meshes where we want
703 : to restrict the calculation to one particular domain. */
704 276240 : std::set<subdomain_id_type> subdomain_id;
705 1518907 : subdomain_id.insert(elem_subid);
706 :
707 1657028 : FEGenericBase<OutputShape> * fe = fe_ptrs[dim].get();
708 276241 : QBase * qrule = q_rules[dim].get();
709 138120 : libmesh_assert(fe);
710 138120 : libmesh_assert(qrule);
711 :
712 : // The Jacobian*weight at the quadrature points.
713 1657028 : const std::vector<Real> & JxW = fe->get_JxW();
714 :
715 : // The value of the shape functions at the quadrature points
716 : // i.e. phi(i) = phi_values[i][qp]
717 138120 : const std::vector<std::vector<OutputShape>> & phi_values = fe->get_phi();
718 :
719 : // The value of the shape function gradients at the quadrature points
720 : const std::vector<std::vector<typename FEGenericBase<OutputShape>::OutputGradient>> &
721 138120 : dphi_values = fe->get_dphi();
722 :
723 : // The value of the shape function curls at the quadrature points
724 : // Only computed for vector-valued elements
725 138120 : const std::vector<std::vector<typename FEGenericBase<OutputShape>::OutputShape>> * curl_values = nullptr;
726 :
727 : // The value of the shape function divergences at the quadrature points
728 : // Only computed for vector-valued elements
729 138120 : const std::vector<std::vector<typename FEGenericBase<OutputShape>::OutputDivergence>> * div_values = nullptr;
730 :
731 1657028 : if (field_type == TYPE_VECTOR)
732 : {
733 177696 : curl_values = &fe->get_curl_phi();
734 177696 : div_values = &fe->get_div_phi();
735 : }
736 :
737 : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
738 : // The value of the shape function second derivatives at the quadrature points
739 : // Not computed for vector-valued elements
740 : const std::vector<std::vector<typename FEGenericBase<OutputShape>::OutputTensor>> *
741 138120 : d2phi_values = nullptr;
742 :
743 1657028 : if (field_type != TYPE_VECTOR)
744 78888 : d2phi_values = &fe->get_d2phi();
745 : #endif
746 :
747 : // The XYZ locations (in physical space) of the quadrature points
748 414185 : const std::vector<Point> & q_point = fe->get_xyz();
749 :
750 : // reinitialize the element-specific data
751 : // for the current element
752 1657028 : fe->reinit (elem);
753 :
754 1657028 : context.pre_fe_reinit(computed_system, elem);
755 1657028 : context.elem_fe_reinit();
756 :
757 : // Get the local to global degree of freedom maps
758 1657028 : computed_dof_map.dof_indices (elem, dof_indices, var);
759 :
760 : // The number of quadrature points
761 138120 : const unsigned int n_qp = qrule->n_points();
762 :
763 : // The number of shape functions
764 : const unsigned int n_sf =
765 276241 : cast_int<unsigned int>(dof_indices.size());
766 :
767 : //
768 : // Begin the loop over the Quadrature points.
769 : //
770 27725399 : for (unsigned int qp=0; qp<n_qp; qp++)
771 : {
772 : // Real u_h = 0.;
773 : // RealGradient grad_u_h;
774 :
775 2172724 : typename FEGenericBase<OutputShape>::OutputNumber u_h(0.);
776 :
777 3060093 : typename FEGenericBase<OutputShape>::OutputNumberGradient grad_u_h;
778 : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
779 4345448 : typename FEGenericBase<OutputShape>::OutputNumberTensor grad2_u_h;
780 : #endif
781 2172724 : typename FEGenericBase<OutputShape>::OutputNumber curl_u_h(0.0);
782 2172724 : typename FEGenericBase<OutputShape>::OutputNumberDivergence div_u_h = 0.0;
783 :
784 : // Compute solution values at the current
785 : // quadrature point. This requires a sum
786 : // over all the shape functions evaluated
787 : // at the quadrature point.
788 245591545 : for (unsigned int i=0; i<n_sf; i++)
789 : {
790 : // Values from current solution.
791 261441256 : u_h += phi_values[i][qp]*computed_system.current_solution (dof_indices[i]);
792 237823688 : grad_u_h += dphi_values[i][qp]*computed_system.current_solution (dof_indices[i]);
793 219523174 : if (field_type == TYPE_VECTOR)
794 : {
795 84392490 : curl_u_h += (*curl_values)[i][qp]*computed_system.current_solution (dof_indices[i]);
796 97375950 : div_u_h += (*div_values)[i][qp]*computed_system.current_solution (dof_indices[i]);
797 : }
798 : else
799 : {
800 : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
801 283244821 : grad2_u_h += (*d2phi_values)[i][qp]*computed_system.current_solution (dof_indices[i]);
802 : #endif
803 : }
804 : }
805 :
806 : // Compute the value of the error at this quadrature point
807 16307312 : typename FEGenericBase<OutputShape>::OutputNumber exact_val(0);
808 2172724 : RawAccessor<typename FEGenericBase<OutputShape>::OutputNumber> exact_val_accessor( exact_val, n_vec_dim );
809 28241098 : if (_exact_values.size() > sys_num && _exact_values[sys_num])
810 : {
811 65304090 : for (unsigned int c = 0; c < n_vec_dim; c++)
812 42034173 : exact_val_accessor(c) =
813 : _exact_values[sys_num]->
814 49041173 : component(context, var_component+c, q_point[qp], time);
815 : }
816 2798454 : else if (_equation_systems_fine)
817 : {
818 : // FIXME: Needs to be updated for vector-valued elements
819 1033560 : DenseVector<Number> output(1);
820 1221480 : (*coarse_values)(q_point[qp],time,output,&subdomain_id);
821 1127520 : exact_val = output(0);
822 : }
823 15017497 : const typename FEGenericBase<OutputShape>::OutputNumber val_error = u_h - exact_val;
824 :
825 : // Add the squares of the error to each contribution
826 2172724 : Real error_sq = TensorTools::norm_sq(val_error);
827 26068371 : error_vals[0] += JxW[qp]*error_sq;
828 :
829 26068371 : Real norm = sqrt(error_sq);
830 26068371 : error_vals[3] += JxW[qp]*norm;
831 :
832 26068371 : if (error_vals[4]<norm) { error_vals[4] = norm; }
833 :
834 : // Compute the value of the error in the gradient at this
835 : // quadrature point
836 3060093 : typename FEGenericBase<OutputShape>::OutputNumberGradient exact_grad;
837 2172724 : RawAccessor<typename FEGenericBase<OutputShape>::OutputNumberGradient> exact_grad_accessor( exact_grad, LIBMESH_DIM );
838 28241098 : if (_exact_derivs.size() > sys_num && _exact_derivs[sys_num])
839 : {
840 65074482 : for (unsigned int c = 0; c < n_vec_dim; c++)
841 167677476 : for (unsigned int d = 0; d < LIBMESH_DIM; d++)
842 136239753 : exact_grad_accessor(d + c*LIBMESH_DIM) =
843 : _exact_derivs[sys_num]->
844 146721417 : component(context, var_component+c, q_point[qp], time)(d);
845 : }
846 2913258 : else if (_equation_systems_fine)
847 : {
848 : // FIXME: Needs to be updated for vector-valued elements
849 1127520 : std::vector<Gradient> output(1);
850 1221480 : coarse_values->gradient(q_point[qp],time,output,&subdomain_id);
851 1127520 : exact_grad = output[0];
852 : }
853 :
854 12821152 : const typename FEGenericBase<OutputShape>::OutputNumberGradient grad_error = grad_u_h - exact_grad;
855 :
856 37114788 : error_vals[1] += JxW[qp]*grad_error.norm_sq();
857 :
858 :
859 26068371 : if (field_type == TYPE_VECTOR)
860 : {
861 : // Compute the value of the error in the curl at this
862 : // quadrature point
863 887369 : typename FEGenericBase<OutputShape>::OutputNumber exact_curl(0.0);
864 11535797 : if (_exact_derivs.size() > sys_num && _exact_derivs[sys_num])
865 : {
866 10648428 : exact_curl = TensorTools::curl_from_grad( exact_grad );
867 : }
868 0 : else if (_equation_systems_fine)
869 : {
870 : // FIXME: Need to implement curl for MeshFunction and support reference
871 : // solution for vector-valued elements
872 : }
873 :
874 887369 : const typename FEGenericBase<OutputShape>::OutputNumber curl_error = curl_u_h - exact_curl;
875 :
876 11535797 : error_vals[5] += JxW[qp]*TensorTools::norm_sq(curl_error);
877 :
878 : // Compute the value of the error in the divergence at this
879 : // quadrature point
880 887369 : typename FEGenericBase<OutputShape>::OutputNumberDivergence exact_div = 0.0;
881 11535797 : if (_exact_derivs.size() > sys_num && _exact_derivs[sys_num])
882 : {
883 10648428 : exact_div = TensorTools::div_from_grad( exact_grad );
884 : }
885 0 : else if (_equation_systems_fine)
886 : {
887 : // FIXME: Need to implement div for MeshFunction and support reference
888 : // solution for vector-valued elements
889 : }
890 :
891 9761059 : const typename FEGenericBase<OutputShape>::OutputNumberDivergence div_error = div_u_h - exact_div;
892 :
893 12423166 : error_vals[6] += JxW[qp]*TensorTools::norm_sq(div_error);
894 : }
895 :
896 : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
897 : // Compute the value of the error in the hessian at this
898 : // quadrature point
899 4345448 : typename FEGenericBase<OutputShape>::OutputNumberTensor exact_hess;
900 2172724 : RawAccessor<typename FEGenericBase<OutputShape>::OutputNumberTensor> exact_hess_accessor( exact_hess, dim );
901 28241098 : if (_exact_hessians.size() > sys_num && _exact_hessians[sys_num])
902 : {
903 : //FIXME: This needs to be implemented to support rank 3 tensors
904 : // which can't happen until type_n_tensor is fully implemented
905 : // and a RawAccessor<TypeNTensor> is fully implemented
906 1670934 : if (field_type == TYPE_VECTOR)
907 0 : libmesh_not_implemented();
908 :
909 3341868 : for (unsigned int c = 0; c < n_vec_dim; c++)
910 5010498 : for (unsigned int d = 0; d < dim; d++)
911 10016388 : for (unsigned int e =0; e < dim; e++)
912 7232068 : exact_hess_accessor(d + e*dim + c*dim*dim) =
913 : _exact_hessians[sys_num]->
914 7787312 : component(context, var_component+c, q_point[qp], time)(d,e);
915 :
916 : // FIXME: operator- is not currently implemented for TypeNTensor
917 1670934 : const typename FEGenericBase<OutputShape>::OutputNumberTensor grad2_error = grad2_u_h - exact_hess;
918 3341868 : error_vals[2] += JxW[qp]*grad2_error.norm_sq();
919 : }
920 24397437 : else if (_equation_systems_fine)
921 : {
922 : // FIXME: Needs to be updated for vector-valued elements
923 1221480 : std::vector<Tensor> output(1);
924 1221480 : coarse_values->hessian(q_point[qp],time,output,&subdomain_id);
925 1127520 : exact_hess = output[0];
926 :
927 : // FIXME: operator- is not currently implemented for TypeNTensor
928 1127520 : const typename FEGenericBase<OutputShape>::OutputNumberTensor grad2_error = grad2_u_h - exact_hess;
929 2255040 : error_vals[2] += JxW[qp]*grad2_error.norm_sq();
930 : }
931 : #endif
932 :
933 : } // end qp loop
934 : } // end element loop
935 :
936 : // Add up the error values on all processors, except for the L-infty
937 : // norm, for which the maximum is computed.
938 27249 : Real l_infty_norm = error_vals[4];
939 26481 : communicator.max(l_infty_norm);
940 27249 : communicator.sum(error_vals);
941 27249 : error_vals[4] = l_infty_norm;
942 27249 : }
943 :
944 : // Explicit instantiations of templated member functions
945 : template LIBMESH_EXPORT void ExactSolution::_compute_error<Real>(std::string_view, std::string_view, std::vector<Real> &);
946 : template LIBMESH_EXPORT void ExactSolution::_compute_error<RealGradient>(std::string_view, std::string_view, std::vector<Real> &);
947 :
948 : } // namespace libMesh
|