LCOV - code coverage report
Current view: top level - src/error_estimation - exact_solution.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4229 (6a9aeb) with base 727f46 Lines: 282 371 76.0 %
Date: 2025-08-19 19:27:09 Functions: 25 47 53.2 %
Legend: Lines: hit not hit

          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

Generated by: LCOV version 1.14