LCOV - code coverage report
Current view: top level - src/systems - system.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4475 (55045b) with base a68cc6 Lines: 722 1125 64.2 %
Date: 2026-06-03 14:29:06 Functions: 105 162 64.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // The libMesh Finite Element Library.
       2             : // Copyright (C) 2002-2026 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             : 
      19             : 
      20             : // Local includes
      21             : #include "libmesh/dof_map.h"
      22             : #include "libmesh/equation_systems.h"
      23             : #include "libmesh/int_range.h"
      24             : #include "libmesh/libmesh_logging.h"
      25             : #include "libmesh/mesh_base.h"
      26             : #include "libmesh/numeric_vector.h"
      27             : #include "libmesh/parameter_vector.h"
      28             : #include "libmesh/point.h"              // For point_value
      29             : #include "libmesh/point_locator_base.h" // For point_value
      30             : #include "libmesh/qoi_set.h"
      31             : #include "libmesh/enum_to_string.h"
      32             : #include "libmesh/sparse_matrix.h"
      33             : #include "libmesh/system.h"
      34             : #include "libmesh/system_norm.h"
      35             : #include "libmesh/utility.h"
      36             : #include "libmesh/elem.h"
      37             : #include "libmesh/fe_type.h"
      38             : #include "libmesh/fe_interface.h"
      39             : #include "libmesh/fe_compute_data.h"
      40             : #include "libmesh/static_condensation.h"
      41             : #include "libmesh/static_condensation_dof_map.h"
      42             : 
      43             : // includes for calculate_norm, point_*
      44             : #include "libmesh/fe_base.h"
      45             : #include "libmesh/fe_interface.h"
      46             : #include "libmesh/parallel.h"
      47             : #include "libmesh/parallel_algebra.h"
      48             : #include "libmesh/quadrature.h"
      49             : #include "libmesh/tensor_value.h"
      50             : #include "libmesh/vector_value.h"
      51             : #include "libmesh/tensor_tools.h"
      52             : #include "libmesh/enum_norm_type.h"
      53             : #include "libmesh/enum_fe_family.h"
      54             : 
      55             : // C++ includes
      56             : #include <sstream>   // for std::ostringstream
      57             : 
      58             : namespace libMesh
      59             : {
      60             : 
      61             : 
      62             : // ------------------------------------------------------------
      63             : // System implementation
      64      242602 : System::System (EquationSystems & es,
      65             :                 const std::string & name_in,
      66      242602 :                 const unsigned int number_in) :
      67             : 
      68             :   ParallelObject                    (es),
      69      228826 :   assemble_before_solve             (true),
      70      228826 :   use_fixed_solution                (false),
      71      228826 :   extra_quadrature_order            (0),
      72      228826 :   solution                          (NumericVector<Number>::build(this->comm())),
      73      228826 :   current_local_solution            (NumericVector<Number>::build(this->comm())),
      74      228826 :   time                              (0.),
      75      228826 :   _init_system_function             (nullptr),
      76      228826 :   _init_system_object               (nullptr),
      77      228826 :   _assemble_system_function         (nullptr),
      78      228826 :   _assemble_system_object           (nullptr),
      79      228826 :   _constrain_system_function        (nullptr),
      80      228826 :   _constrain_system_object          (nullptr),
      81      228826 :   _qoi_evaluate_function            (nullptr),
      82      228826 :   _qoi_evaluate_object              (nullptr),
      83      228826 :   _qoi_evaluate_derivative_function (nullptr),
      84      228826 :   _qoi_evaluate_derivative_object   (nullptr),
      85      228826 :   _dof_map                          (std::make_unique<DofMap>(number_in, es.get_mesh())),
      86      228826 :   _equation_systems                 (es),
      87      249490 :   _mesh                             (es.get_mesh()),
      88      228826 :   _sys_name                         (name_in),
      89      228826 :   _sys_number                       (number_in),
      90      228826 :   _active                           (true),
      91      228826 :   _matrices_initialized             (false),
      92      228826 :   _solution_projection              (true),
      93      228826 :   _basic_system_only                (false),
      94      228826 :   _is_initialized                   (false),
      95      228826 :   _additional_data_written          (false),
      96      228826 :   adjoint_already_solved            (false),
      97      228826 :   _hide_output                      (false),
      98      228826 :   project_with_constraints          (true),
      99      228826 :   _prefer_hash_table_matrix_assembly(false),
     100      228826 :   _require_sparsity_pattern         (false),
     101      249490 :   _prefix_with_name                 (false)
     102             : {
     103      478316 :   if (libMesh::on_command_line("--solver-system-names"))
     104           0 :     this->prefix_with_name(true);
     105      707142 :   if (libMesh::on_command_line("--" + name_in + "-static-condensation"))
     106         420 :     this->create_static_condensation();
     107      242602 : }
     108             : 
     109             : 
     110             : 
     111      679434 : System::~System ()
     112             : {
     113        6888 :   libmesh_exceptionless_assert (!libMesh::closed());
     114      450608 : }
     115             : 
     116             : 
     117             : 
     118     1423402 : dof_id_type System::n_dofs() const
     119             : {
     120     1423402 :   return _dof_map->n_dofs();
     121             : }
     122             : 
     123             : 
     124             : 
     125       50957 : dof_id_type System::n_constrained_dofs() const
     126             : {
     127             : #ifdef LIBMESH_ENABLE_CONSTRAINTS
     128             : 
     129       50957 :   return _dof_map->n_constrained_dofs();
     130             : 
     131             : #else
     132             : 
     133             :   return 0;
     134             : 
     135             : #endif
     136             : }
     137             : 
     138             : 
     139             : 
     140       20102 : dof_id_type System::n_local_constrained_dofs() const
     141             : {
     142             : #ifdef LIBMESH_ENABLE_CONSTRAINTS
     143             : 
     144       20102 :   return _dof_map->n_local_constrained_dofs();
     145             : 
     146             : #else
     147             : 
     148             :   return 0;
     149             : 
     150             : #endif
     151             : }
     152             : 
     153             : 
     154             : 
     155     1404299 : dof_id_type System::n_local_dofs() const
     156             : {
     157     1404299 :   return _dof_map->n_local_dofs();
     158             : }
     159             : 
     160             : 
     161             : 
     162  1076433086 : Number System::current_solution (const dof_id_type global_dof_number) const
     163             : {
     164             :   // Check the sizes
     165    96669372 :   libmesh_assert_less (global_dof_number, _dof_map->n_dofs());
     166    96669372 :   libmesh_assert_less (global_dof_number, current_local_solution->size());
     167             : 
     168  1076433086 :   return (*current_local_solution)(global_dof_number);
     169             : }
     170             : 
     171             : 
     172             : 
     173         919 : void System::clear ()
     174             : {
     175         919 :   _dof_map->clear ();
     176         919 :   solution->clear ();
     177         919 :   current_local_solution->clear ();
     178             : 
     179             :   // clear any user-added vectors
     180          28 :   _vectors.clear();
     181          28 :   _vector_projections.clear();
     182          28 :   _vector_is_adjoint.clear();
     183         919 :   _is_initialized = false;
     184             : 
     185             :   // clear any user-added matrices
     186          28 :   _matrices.clear();
     187         919 :   _matrices_initialized = false;
     188             : 
     189             :   // But our "basic"/"null" state may still have a StaticCondensation
     190        2673 :   if (libMesh::on_command_line("--" + _sys_name + "-static-condensation"))
     191          70 :     this->create_static_condensation();
     192         919 : }
     193             : 
     194             : 
     195             : 
     196         633 : void System::init ()
     197             : {
     198             :   // Calling init() twice on the same system currently works evil
     199             :   // magic, whether done directly or via EquationSystems::read()
     200          18 :   libmesh_assert(!this->is_initialized());
     201             : 
     202         633 :   this->reinit_mesh();
     203         633 : }
     204             : 
     205             : 
     206             : 
     207      242881 : void System::init_data ()
     208             : {
     209        6886 :   parallel_object_only();
     210             : 
     211       13772 :   MeshBase & mesh = this->get_mesh();
     212             : 
     213             :   // Distribute the degrees of freedom on the mesh
     214      242881 :   auto total_dofs = _dof_map->distribute_dofs (mesh);
     215             : 
     216             :   // Throw an error if the total number of DOFs is not capable of
     217             :   // being indexed by our solution vector.
     218      242858 :   auto max_allowed_id = solution->max_allowed_id();
     219      242858 :   libmesh_error_msg_if(total_dofs > max_allowed_id,
     220             :                        "Cannot allocate a NumericVector with " << total_dofs << " degrees of freedom. "
     221             :                        "The vector can only index up to " << max_allowed_id << " entries.");
     222             : 
     223             :   // Recreate any user or internal constraints
     224      242858 :   this->reinit_constraints();
     225             : 
     226             :   // Even if there weren't any constraint changes,
     227             :   // reinit_constraints() did prepare_send_list() for us.
     228             : 
     229             :   // Now finally after dof distribution and construction of any
     230             :   // possible constraints, we may init any static condensation
     231             :   // data
     232      242787 :   _dof_map->reinit_static_condensation();
     233             : 
     234             :   // Resize the solution conformal to the current mesh
     235      242787 :   solution->init (this->n_dofs(), this->n_local_dofs(), false, PARALLEL);
     236             : 
     237             :   // Resize the current_local_solution for the current mesh
     238             : #ifdef LIBMESH_ENABLE_GHOSTED
     239      249669 :   current_local_solution->init (this->n_dofs(), this->n_local_dofs(),
     240             :                                 _dof_map->get_send_list(), /*fast=*/false,
     241       13764 :                                 GHOSTED);
     242             : #else
     243             :   current_local_solution->init (this->n_dofs(), false, SERIAL);
     244             : #endif
     245             : 
     246             :   // from now on, adding additional vectors or variables can't be done
     247             :   // without immediately initializing them
     248      242787 :   _is_initialized = true;
     249             : 
     250             :   // initialize & zero other vectors, if necessary
     251      287429 :   for (auto & [vec_name, vec] : _vectors)
     252             :     {
     253        1330 :       libmesh_ignore(vec_name); // spurious warning from old gcc
     254       44642 :       const ParallelType type = vec->type();
     255             : 
     256       44642 :       if (type == GHOSTED)
     257             :         {
     258             : #ifdef LIBMESH_ENABLE_GHOSTED
     259        6112 :           vec->init (this->n_dofs(), this->n_local_dofs(),
     260             :                            _dof_map->get_send_list(), /*fast=*/false,
     261         344 :                            GHOSTED);
     262             : #else
     263             :           libmesh_error_msg("Cannot initialize ghosted vectors when they are not enabled.");
     264             : #endif
     265             :         }
     266       38702 :       else if (type == SERIAL)
     267             :         {
     268           0 :           vec->init (this->n_dofs(), false, type);
     269             :         }
     270             :       else
     271             :         {
     272        1158 :           libmesh_assert_equal_to(type, PARALLEL);
     273       38702 :           vec->init (this->n_dofs(), this->n_local_dofs(), false, type);
     274             :         }
     275             :     }
     276             : 
     277             :   // Add matrices
     278      242787 :   this->add_matrices();
     279             : 
     280             :   // Clear any existing matrices
     281      263651 :   for (auto & pr : _matrices)
     282       20864 :     pr.second->clear();
     283             : 
     284             :   // Initialize the matrices for the system
     285      242787 :   if (!_basic_system_only)
     286      242787 :    this->init_matrices();
     287      242787 : }
     288             : 
     289      242881 : void System::reinit_mesh ()
     290             : {
     291        6886 :   parallel_object_only();
     292             : 
     293             :   // First initialize any required data:
     294             :   // either only the basic System data
     295      242881 :   if (_basic_system_only)
     296           0 :     System::init_data();
     297             :   // or all the derived class' data too
     298             :   else
     299      242881 :     this->init_data();
     300             : 
     301             :   // If no variables have been added to this system
     302             :   // don't do anything
     303      242787 :   if (!this->n_vars())
     304          28 :     return;
     305             : 
     306             :   // Then call the user-provided initialization function
     307      241804 :   this->user_initialization();
     308             : 
     309             : }
     310             : 
     311      242787 : void System::init_matrices ()
     312             : {
     313        6882 :   parallel_object_only();
     314             : 
     315             :   // No matrices to init
     316      242787 :   if (_matrices.empty())
     317             :     {
     318             :       // any future matrices to be added will need their own
     319             :       // initialization
     320      222562 :       _matrices_initialized = true;
     321             : 
     322      222562 :       return;
     323             :     }
     324             : 
     325             :   // Check for quick return in case the first matrix
     326             :   // (and by extension all the matrices) has already
     327             :   // been initialized
     328       20225 :   if (_matrices.begin()->second->initialized())
     329             :     {
     330           0 :       libmesh_assert(_matrices_initialized);
     331           0 :       return;
     332             :     }
     333             : 
     334       20225 :   _matrices_initialized = true;
     335             : 
     336             :   // Tell the matrices about the dof map, and vice versa
     337       41089 :   for (auto & pr : _matrices)
     338             :     {
     339         580 :       SparseMatrix<Number> & m = *(pr.second);
     340         580 :       libmesh_assert (!m.initialized());
     341             : 
     342             :       // We want to allow repeated init() on systems, but we don't
     343             :       // want to attach the same matrix to the DofMap twice
     344       20864 :       if (!this->get_dof_map().is_attached(m))
     345       20864 :         this->get_dof_map().attach_matrix(m);
     346             : 
     347             :       // If the user has already explicitly requested that this matrix use a hash table, then we
     348             :       // always honor that
     349             :       const bool use_hash =
     350       21444 :           pr.second->use_hash_table() ||
     351       20798 :           (this->_prefer_hash_table_matrix_assembly && pr.second->supports_hash_table());
     352       20864 :       pr.second->use_hash_table(use_hash);
     353             :       // Make this call after we've determined whether the matrix is using a hash table
     354       20864 :       if (pr.second->require_sparsity_pattern())
     355       20242 :         this->_require_sparsity_pattern = true;
     356             :     }
     357             : 
     358             :   // Compute the sparsity pattern for the current
     359             :   // mesh and DOF distribution.  This also updates
     360             :   // additional matrices, \p DofMap now knows them
     361       20225 :   if (this->_require_sparsity_pattern)
     362       19669 :     this->get_dof_map().compute_sparsity(this->get_mesh());
     363             : 
     364             :   // Initialize matrices and set to zero
     365       41089 :   for (auto & [name, mat] : _matrices)
     366             :     {
     367       20864 :       mat->init(_matrix_types[name]);
     368       20864 :       mat->zero();
     369             :     }
     370             : }
     371             : 
     372             : 
     373             : 
     374       31142 : void System::restrict_vectors ()
     375             : {
     376        1000 :   parallel_object_only();
     377             : 
     378             : #ifdef LIBMESH_ENABLE_AMR
     379             :   // Restrict the _vectors on the coarsened cells
     380       90975 :   for (auto & [vec_name, vec] : _vectors)
     381             :     {
     382        2130 :       NumericVector<Number> * v = vec.get();
     383             : 
     384       59833 :       if (_vector_projections[vec_name])
     385             :         {
     386       53730 :           this->project_vector (*v, this->vector_is_adjoint(vec_name));
     387             :         }
     388             :       else
     389             :         {
     390       32458 :           const ParallelType type = vec->type();
     391             : 
     392       32458 :           if (type == GHOSTED)
     393             :             {
     394             : #ifdef LIBMESH_ENABLE_GHOSTED
     395           0 :               vec->init (this->n_dofs(), this->n_local_dofs(),
     396             :                                _dof_map->get_send_list(), /*fast=*/false,
     397           0 :                                GHOSTED);
     398             : #else
     399             :               libmesh_error_msg("Cannot initialize ghosted vectors when they are not enabled.");
     400             : #endif
     401             :             }
     402             :           else
     403       32458 :             vec->init (this->n_dofs(), this->n_local_dofs(), false, type);
     404             :         }
     405             :     }
     406             : 
     407        1000 :   const std::vector<dof_id_type> & send_list = _dof_map->get_send_list ();
     408             : 
     409             :   // Restrict the solution on the coarsened cells
     410       31142 :   if (_solution_projection)
     411       59604 :     this->project_vector (*solution);
     412             :   // Or at least make sure the solution vector is the correct size
     413             :   else
     414         852 :     solution->init (this->n_dofs(), this->n_local_dofs(), true, PARALLEL);
     415             : 
     416             : #ifdef LIBMESH_ENABLE_GHOSTED
     417       32142 :   current_local_solution->init(this->n_dofs(),
     418             :                                this->n_local_dofs(), send_list,
     419        2000 :                                false, GHOSTED);
     420             : #else
     421             :   current_local_solution->init(this->n_dofs());
     422             : #endif
     423             : 
     424       31142 :   if (_solution_projection)
     425       31266 :     solution->localize (*current_local_solution, send_list);
     426             : 
     427             : #endif // LIBMESH_ENABLE_AMR
     428       31142 : }
     429             : 
     430             : 
     431             : 
     432       31142 : void System::prolong_vectors ()
     433             : {
     434             : #ifdef LIBMESH_ENABLE_AMR
     435             :   // Currently project_vector handles both restriction and prolongation
     436       31142 :   this->restrict_vectors();
     437             : #endif
     438       31142 : }
     439             : 
     440             : 
     441             : 
     442       30000 : void System::reinit ()
     443             : {
     444         966 :   parallel_object_only();
     445             : 
     446             :   // project_vector handles vector initialization now
     447         966 :   libmesh_assert_equal_to (solution->size(), current_local_solution->size());
     448             : 
     449             :   // Make sure our static condensation dof map is up-to-date before we init any
     450             :   // static condensation matrices
     451       30000 :   this->get_dof_map().reinit_static_condensation();
     452             : 
     453       30000 :   if (!_matrices.empty() && !_basic_system_only)
     454             :     {
     455             :       // Clear the matrices
     456       46814 :       for (auto & pr : _matrices)
     457             :         {
     458       23477 :           pr.second->clear();
     459       23477 :           pr.second->attach_dof_map(this->get_dof_map());
     460             :         }
     461             : 
     462       23337 :       if (this->_require_sparsity_pattern)
     463             :         {
     464             :           // Clear the sparsity pattern
     465       19697 :           this->get_dof_map().clear_sparsity();
     466             : 
     467             :           // Compute the sparsity pattern for the current
     468             :           // mesh and DOF distribution.  This also updates
     469             :           // additional matrices, \p DofMap now knows them
     470       19697 :           this->get_dof_map().compute_sparsity (this->get_mesh());
     471             :         }
     472             : 
     473             :       // Initialize matrices and set to zero
     474       46814 :       for (auto & pr : _matrices)
     475             :         {
     476       23477 :           pr.second->init();
     477       23477 :           pr.second->zero();
     478             :         }
     479             :     }
     480       30000 : }
     481             : 
     482             : 
     483      274280 : void System::reinit_constraints()
     484             : {
     485        7892 :   parallel_object_only();
     486             : 
     487             : #ifdef LIBMESH_ENABLE_CONSTRAINTS
     488      274280 :   get_dof_map().create_dof_constraints(_mesh, this->time);
     489      274280 :   user_constrain();
     490      274280 :   get_dof_map().process_constraints(_mesh);
     491      540528 :   if (libMesh::on_command_line ("--print-constraints"))
     492           0 :     get_dof_map().print_dof_constraints(libMesh::out);
     493             : #endif
     494      274209 :   get_dof_map().prepare_send_list();
     495      274209 : }
     496             : 
     497             : 
     498     1463490 : void System::update ()
     499             : {
     500       37556 :   parallel_object_only();
     501             : 
     502       37556 :   libmesh_assert(solution->closed());
     503             : 
     504       37556 :   const std::vector<dof_id_type> & send_list = _dof_map->get_send_list ();
     505             : 
     506             :   // Check sizes
     507       37556 :   libmesh_assert_equal_to (current_local_solution->size(), solution->size());
     508             :   // More processors than elements => empty send_list
     509             :   //  libmesh_assert (!send_list.empty());
     510       37556 :   libmesh_assert_less_equal (send_list.size(), solution->size());
     511             : 
     512             :   // Create current_local_solution from solution.  This will
     513             :   // put a local copy of solution into current_local_solution.
     514             :   // Only the necessary values (specified by the send_list)
     515             :   // are copied to minimize communication
     516     1500230 :   solution->localize (*current_local_solution, send_list);
     517     1463490 : }
     518             : 
     519             : 
     520             : 
     521       29935 : void System::re_update ()
     522             : {
     523         966 :   parallel_object_only();
     524             : 
     525             :   // If this system is empty... don't do anything!
     526       29935 :   if (!this->n_vars())
     527         108 :     return;
     528             : 
     529         858 :   const std::vector<dof_id_type> & send_list = this->get_dof_map().get_send_list ();
     530             : 
     531             :   // Check sizes
     532         858 :   libmesh_assert_equal_to (current_local_solution->size(), solution->size());
     533             :   // Not true with ghosted vectors
     534             :   // libmesh_assert_equal_to (current_local_solution->local_size(), solution->size());
     535             :   // libmesh_assert (!send_list.empty());
     536         858 :   libmesh_assert_less_equal (send_list.size(), solution->size());
     537             : 
     538             :   // Create current_local_solution from solution.  This will
     539             :   // put a local copy of solution into current_local_solution.
     540       27011 :   solution->localize (*current_local_solution, send_list);
     541             : }
     542             : 
     543             : 
     544             : 
     545           0 : void System::restrict_solve_to (const SystemSubset * subset,
     546             :                                 const SubsetSolveMode /*subset_solve_mode*/)
     547             : {
     548           0 :   if (subset != nullptr)
     549           0 :     libmesh_not_implemented();
     550           0 : }
     551             : 
     552             : 
     553             : 
     554       46331 : void System::assemble ()
     555             : {
     556             :   // Log how long the user's assembly code takes
     557        3088 :   LOG_SCOPE("assemble()", "System");
     558             : 
     559             :   // Call the user-specified assembly function
     560       46331 :   this->user_assembly();
     561       46331 : }
     562             : 
     563             : 
     564             : 
     565           0 : void System::assemble_qoi (const QoISet & qoi_indices)
     566             : {
     567             :   // Log how long the user's assembly code takes
     568           0 :   LOG_SCOPE("assemble_qoi()", "System");
     569             : 
     570             :   // Call the user-specified quantity of interest function
     571           0 :   this->user_QOI(qoi_indices);
     572           0 : }
     573             : 
     574             : 
     575             : 
     576           0 : void System::assemble_qoi_derivative(const QoISet & qoi_indices,
     577             :                                      bool include_liftfunc,
     578             :                                      bool apply_constraints)
     579             : {
     580             :   // Log how long the user's assembly code takes
     581           0 :   LOG_SCOPE("assemble_qoi_derivative()", "System");
     582             : 
     583             :   // Call the user-specified quantity of interest function
     584           0 :   this->user_QOI_derivative(qoi_indices, include_liftfunc,
     585           0 :                             apply_constraints);
     586           0 : }
     587             : 
     588             : 
     589             : 
     590           0 : void System::qoi_parameter_sensitivity (const QoISet & qoi_indices,
     591             :                                         const ParameterVector & parameters_vec,
     592             :                                         SensitivityData & sensitivities)
     593             : {
     594             :   // Forward sensitivities are more efficient for Nq > Np
     595           0 :   if (qoi_indices.size(*this) > parameters_vec.size())
     596           0 :     forward_qoi_parameter_sensitivity(qoi_indices, parameters_vec, sensitivities);
     597             :   // Adjoint sensitivities are more efficient for Np > Nq,
     598             :   // and an adjoint may be more reusable than a forward
     599             :   // solution sensitivity in the Np == Nq case.
     600             :   else
     601           0 :     adjoint_qoi_parameter_sensitivity(qoi_indices, parameters_vec, sensitivities);
     602           0 : }
     603             : 
     604             : 
     605             : 
     606           0 : bool System::compare (const System & other_system,
     607             :                       const Real threshold,
     608             :                       const bool verbose) const
     609             : {
     610             :   // we do not care for matrices, but for vectors
     611           0 :   libmesh_assert (_is_initialized);
     612           0 :   libmesh_assert (other_system._is_initialized);
     613             : 
     614           0 :   if (verbose)
     615             :     {
     616           0 :       libMesh::out << "  Systems \"" << _sys_name << "\"" << std::endl;
     617           0 :       libMesh::out << "   comparing matrices not supported." << std::endl;
     618           0 :       libMesh::out << "   comparing names...";
     619             :     }
     620             : 
     621             :   // compare the name: 0 means identical
     622           0 :   const int name_result = _sys_name.compare(other_system.name());
     623           0 :   if (verbose)
     624             :     {
     625           0 :       if (name_result == 0)
     626           0 :         libMesh::out << " identical." << std::endl;
     627             :       else
     628           0 :         libMesh::out << "  names not identical." << std::endl;
     629           0 :       libMesh::out << "   comparing solution vector...";
     630             :     }
     631             : 
     632             : 
     633             :   // compare the solution: -1 means identical
     634           0 :   const int solu_result = solution->compare (*other_system.solution.get(),
     635           0 :                                              threshold);
     636             : 
     637           0 :   if (verbose)
     638             :     {
     639           0 :       if (solu_result == -1)
     640           0 :         libMesh::out << " identical up to threshold." << std::endl;
     641             :       else
     642           0 :         libMesh::out << "  first difference occurred at index = "
     643           0 :                      << solu_result << "." << std::endl;
     644             :     }
     645             : 
     646             : 
     647             :   // safety check, whether we handle at least the same number
     648             :   // of vectors
     649           0 :   std::vector<int> ov_result;
     650             : 
     651           0 :   if (this->n_vectors() != other_system.n_vectors())
     652             :     {
     653           0 :       if (verbose)
     654             :         {
     655           0 :           libMesh::out << "   Fatal difference. This system handles "
     656           0 :                        << this->n_vectors() << " add'l vectors," << std::endl
     657           0 :                        << "   while the other system handles "
     658           0 :                        << other_system.n_vectors()
     659           0 :                        << " add'l vectors." << std::endl
     660           0 :                        << "   Aborting comparison." << std::endl;
     661             :         }
     662           0 :       return false;
     663             :     }
     664           0 :   else if (this->n_vectors() == 0)
     665             :     {
     666             :       // there are no additional vectors...
     667           0 :       ov_result.clear ();
     668             :     }
     669             :   else
     670             :     {
     671             :       // compare other vectors
     672           0 :       for (auto & [vec_name, vec] : _vectors)
     673             :         {
     674           0 :           if (verbose)
     675           0 :             libMesh::out << "   comparing vector \""
     676           0 :                          << vec_name << "\" ...";
     677             : 
     678             :           // assume they have the same name
     679             :           const NumericVector<Number> & other_system_vector =
     680           0 :             other_system.get_vector(vec_name);
     681             : 
     682           0 :           ov_result.push_back(vec->compare(other_system_vector, threshold));
     683             : 
     684           0 :           if (verbose)
     685             :             {
     686           0 :               if (ov_result[ov_result.size()-1] == -1)
     687           0 :                 libMesh::out << " identical up to threshold." << std::endl;
     688             :               else
     689           0 :                 libMesh::out << " first difference occurred at" << std::endl
     690           0 :                              << "   index = " << ov_result[ov_result.size()-1] << "." << std::endl;
     691             :             }
     692             :         }
     693             :     } // finished comparing additional vectors
     694             : 
     695             : 
     696             :   bool overall_result;
     697             : 
     698             :   // sum up the results
     699           0 :   if ((name_result==0) && (solu_result==-1))
     700             :     {
     701           0 :       if (ov_result.size()==0)
     702           0 :         overall_result = true;
     703             :       else
     704             :         {
     705             :           bool ov_identical;
     706           0 :           unsigned int n    = 0;
     707           0 :           do
     708             :             {
     709           0 :               ov_identical = (ov_result[n]==-1);
     710           0 :               n++;
     711             :             }
     712           0 :           while (ov_identical && n<ov_result.size());
     713           0 :           overall_result = ov_identical;
     714           0 :         }
     715             :     }
     716             :   else
     717           0 :     overall_result = false;
     718             : 
     719           0 :   if (verbose)
     720             :     {
     721           0 :       libMesh::out << "   finished comparisons, ";
     722           0 :       if (overall_result)
     723           0 :         libMesh::out << "found no differences." << std::endl << std::endl;
     724             :       else
     725           0 :         libMesh::out << "found differences." << std::endl << std::endl;
     726             :     }
     727             : 
     728           0 :   return overall_result;
     729             : }
     730             : 
     731             : 
     732             : 
     733          71 : void System::update_global_solution (std::vector<Number> & global_soln) const
     734             : {
     735           2 :   parallel_object_only();
     736             : 
     737          71 :   global_soln.resize (solution->size());
     738             : 
     739          71 :   solution->localize (global_soln);
     740          71 : }
     741             : 
     742             : 
     743             : 
     744        5578 : void System::update_global_solution (std::vector<Number> & global_soln,
     745             :                                      const processor_id_type dest_proc) const
     746             : {
     747         232 :   parallel_object_only();
     748             : 
     749        5578 :   global_soln.resize        (solution->size());
     750             : 
     751        5578 :   solution->localize_to_one (global_soln, dest_proc);
     752        5578 : }
     753             : 
     754             : 
     755             : 
     756      210862 : NumericVector<Number> & System::add_vector (std::string_view vec_name,
     757             :                                             const bool projections,
     758             :                                             const ParallelType type)
     759             : {
     760        6266 :   parallel_object_only();
     761             : 
     762        6266 :   libmesh_assert(this->comm().verify(std::string(vec_name)));
     763        6266 :   libmesh_assert(this->comm().verify(int(type)));
     764        6266 :   libmesh_assert(this->comm().verify(projections));
     765             : 
     766             :   // Return the vector if it is already there.
     767      210862 :   if (auto it = this->_vectors.find(vec_name);
     768        6266 :       it != this->_vectors.end())
     769             :     {
     770             :       // If the projection setting has *upgraded*, change it.
     771      119165 :       if (projections) // only do expensive lookup if needed
     772       46734 :         libmesh_map_find(_vector_projections, vec_name) = projections;
     773             : 
     774        3550 :       NumericVector<Number> & vec = *it->second;
     775             : 
     776             :       // If we're in serial, our vectors are effectively SERIAL, so
     777             :       // we'll ignore any type setting.  If we're in parallel, we
     778             :       // might have a type change to deal with.
     779             : 
     780      122715 :       if (this->n_processors() > 1)
     781             :         {
     782             :           // If the type setting has changed in a way we can't
     783             :           // perceive as an upgrade or a downgrade, scream.
     784        3550 :           libmesh_assert_equal_to(type == SERIAL,
     785             :                                   vec.type() == SERIAL);
     786             : 
     787             :           // If the type setting has *upgraded*, change it.
     788      117339 :           if (type == GHOSTED && vec.type() == PARALLEL)
     789             :             {
     790             :               // A *really* late upgrade is expensive, but better not
     791             :               // to risk zeroing data.
     792         138 :               if (vec.initialized())
     793             :                 {
     794          69 :                   if (!vec.closed())
     795           0 :                     vec.close();
     796             : 
     797             :                   // Ideally we'd move parallel coefficients and then
     798             :                   // add ghosted coefficients, but copy and swap is
     799             :                   // simpler.  If anyone actually ever uses this case
     800             :                   // for real we can look into optimizing it.
     801          71 :                   auto new_vec = NumericVector<Number>::build(this->comm());
     802             : #ifdef LIBMESH_ENABLE_GHOSTED
     803          71 :                   new_vec->init (this->n_dofs(), this->n_local_dofs(),
     804             :                                  _dof_map->get_send_list(), /*fast=*/false,
     805           4 :                                  GHOSTED);
     806             : #else
     807             :                   libmesh_error_msg("Cannot initialize ghosted vectors when they are not enabled.");
     808             : #endif
     809             : 
     810          69 :                   *new_vec = vec;
     811          71 :                   vec.swap(*new_vec);
     812          65 :                 }
     813             :               else
     814             :                 // The PARALLEL vec is not yet initialized, so we can
     815             :                 // just "upgrade" it to GHOSTED.
     816          69 :                 vec.set_type(type);
     817             :             }
     818             :         }
     819             : 
     820             :       // Any upgrades are done; we're happy here.
     821        3550 :       return vec;
     822             :     }
     823             : 
     824             :   // Otherwise, build the vector. The following emplace() is
     825             :   // guaranteed to succeed because, if we made it here, we don't
     826             :   // already have a vector named "vec_name". We pass the user's
     827             :   // requested ParallelType directly to NumericVector::build() so
     828             :   // that, even if the vector is not initialized now, it will get the
     829             :   // right type when it is initialized later.
     830             :   auto pr =
     831             :     _vectors.emplace(vec_name,
     832      183394 :                      NumericVector<Number>::build(this->comm(),
     833             :                                                   libMesh::default_solver_package(),
     834        5432 :                                                   type));
     835        2716 :   auto buf = pr.first->second.get();
     836        2716 :   _vector_projections.emplace(vec_name, projections);
     837             : 
     838             :   // Vectors are primal by default
     839       91697 :   _vector_is_adjoint.emplace(vec_name, -1);
     840             : 
     841             :   // Initialize it if necessary
     842       91697 :   if (_is_initialized)
     843             :     {
     844       45702 :       if (type == GHOSTED)
     845             :         {
     846             : #ifdef LIBMESH_ENABLE_GHOSTED
     847        4200 :           buf->init (this->n_dofs(), this->n_local_dofs(),
     848             :                      _dof_map->get_send_list(), /*fast=*/false,
     849         240 :                      GHOSTED);
     850             : #else
     851             :           libmesh_error_msg("Cannot initialize ghosted vectors when they are not enabled.");
     852             : #endif
     853             :         }
     854             :       else
     855       41502 :         buf->init (this->n_dofs(), this->n_local_dofs(), false, type);
     856             :     }
     857             : 
     858        2716 :   return *buf;
     859             : }
     860             : 
     861       39413 : void System::remove_vector (std::string_view vec_name)
     862             : {
     863        1126 :   parallel_object_only();  // Not strictly needed, but the only safe way to keep in sync
     864             : 
     865       39413 :   if (const auto pos = _vectors.find(vec_name);
     866        1126 :       pos != _vectors.end())
     867             :     {
     868       38287 :       _vectors.erase(pos);
     869        1126 :       auto proj_it = _vector_projections.find(vec_name);
     870        1126 :       libmesh_assert(proj_it != _vector_projections.end());
     871       38287 :       _vector_projections.erase(proj_it);
     872             : 
     873        1126 :       auto adj_it = _vector_is_adjoint.find(vec_name);
     874        1126 :       libmesh_assert(adj_it != _vector_is_adjoint.end());
     875       38287 :       _vector_is_adjoint.erase(adj_it);
     876             :     }
     877       39413 : }
     878             : 
     879           0 : const NumericVector<Number> * System::request_vector (std::string_view vec_name) const
     880             : {
     881           0 :   if (const auto pos = _vectors.find(vec_name);
     882           0 :       pos != _vectors.end())
     883           0 :     return pos->second.get();
     884             : 
     885             :   // Otherwise, vec_name was not found
     886           0 :   return nullptr;
     887             : }
     888             : 
     889             : 
     890             : 
     891           0 : NumericVector<Number> * System::request_vector (std::string_view vec_name)
     892             : {
     893           0 :   if (auto pos = _vectors.find(vec_name);
     894           0 :       pos != _vectors.end())
     895           0 :     return pos->second.get();
     896             : 
     897             :   // Otherwise, vec_name was not found
     898           0 :   return nullptr;
     899             : }
     900             : 
     901             : 
     902             : 
     903           0 : const NumericVector<Number> * System::request_vector (const unsigned int vec_num) const
     904             : {
     905             :   // If we don't have that many vectors, return nullptr
     906           0 :   if (vec_num >= _vectors.size())
     907           0 :     return nullptr;
     908             : 
     909             :   // Otherwise return a pointer to the vec_num'th vector
     910           0 :   auto it = vectors_begin();
     911           0 :   std::advance(it, vec_num);
     912           0 :   return it->second.get();
     913             : }
     914             : 
     915             : 
     916             : 
     917           0 : NumericVector<Number> * System::request_vector (const unsigned int vec_num)
     918             : {
     919             :   // If we don't have that many vectors, return nullptr
     920           0 :   if (vec_num >= _vectors.size())
     921           0 :     return nullptr;
     922             : 
     923             :   // Otherwise return a pointer to the vec_num'th vector
     924           0 :   auto it = vectors_begin();
     925           0 :   std::advance(it, vec_num);
     926           0 :   return it->second.get();
     927             : }
     928             : 
     929             : 
     930             : 
     931        5792 : const NumericVector<Number> & System::get_vector (std::string_view vec_name) const
     932             : {
     933        5792 :   return *(libmesh_map_find(_vectors, vec_name));
     934             : }
     935             : 
     936             : 
     937             : 
     938     6382872 : NumericVector<Number> & System::get_vector (std::string_view vec_name)
     939             : {
     940     6382872 :   return *(libmesh_map_find(_vectors, vec_name));
     941             : }
     942             : 
     943             : 
     944             : 
     945           0 : const NumericVector<Number> & System::get_vector (const unsigned int vec_num) const
     946             : {
     947             :   // If we don't have that many vectors, throw an error
     948           0 :   libmesh_assert_less(vec_num, _vectors.size());
     949             : 
     950             :   // Otherwise return a reference to the vec_num'th vector
     951           0 :   auto it = vectors_begin();
     952           0 :   std::advance(it, vec_num);
     953           0 :   return *(it->second);
     954             : }
     955             : 
     956             : 
     957             : 
     958           0 : NumericVector<Number> & System::get_vector (const unsigned int vec_num)
     959             : {
     960             :   // If we don't have that many vectors, throw an error
     961           0 :   libmesh_assert_less(vec_num, _vectors.size());
     962             : 
     963             :   // Otherwise return a reference to the vec_num'th vector
     964           0 :   auto it = vectors_begin();
     965           0 :   std::advance(it, vec_num);
     966           0 :   return *(it->second);
     967             : }
     968             : 
     969             : 
     970             : 
     971           0 : const std::string & System::vector_name (const unsigned int vec_num) const
     972             : {
     973             :   // If we don't have that many vectors, throw an error
     974           0 :   libmesh_assert_less(vec_num, _vectors.size());
     975             : 
     976             :   // Otherwise return a reference to the vec_num'th vector name
     977           0 :   auto it = vectors_begin();
     978           0 :   std::advance(it, vec_num);
     979           0 :   return it->first;
     980             : }
     981             : 
     982       66238 : const std::string & System::vector_name (const NumericVector<Number> & vec_reference) const
     983             : {
     984             :   // Linear search for a vector whose pointer matches vec_reference
     985       62454 :   auto it = std::find_if(vectors_begin(), vectors_end(),
     986       31852 :                          [&vec_reference](const decltype(_vectors)::value_type & pr)
     987       17818 :                          { return &vec_reference == pr.second.get(); });
     988             : 
     989             :   // Before returning, make sure we didn't loop till the end and not find any match
     990        1892 :   libmesh_assert (it != vectors_end());
     991             : 
     992             :   // Return the string associated with the current vector
     993       66238 :   return it->first;
     994             : }
     995             : 
     996             : 
     997             : 
     998       20374 : SparseMatrix<Number> & System::add_matrix (std::string_view mat_name,
     999             :                                            const ParallelType type,
    1000             :                                            const MatrixBuildType mat_build_type)
    1001             : {
    1002         566 :   parallel_object_only();
    1003             : 
    1004         566 :   libmesh_assert(this->comm().verify(std::string(mat_name)));
    1005         566 :   libmesh_assert(this->comm().verify(int(type)));
    1006         566 :   libmesh_assert(this->comm().verify(int(mat_build_type)));
    1007             : 
    1008             :   // Return the matrix if it is already there.
    1009       20374 :   if (auto it = this->_matrices.find(mat_name);
    1010         566 :       it != this->_matrices.end())
    1011           0 :     return *it->second;
    1012             : 
    1013             :   // Otherwise build the matrix to return.
    1014       19808 :   std::unique_ptr<SparseMatrix<Number>> matrix;
    1015       20374 :   if (this->has_static_condensation())
    1016             :     {
    1017           0 :       if (mat_build_type == MatrixBuildType::DIAGONAL)
    1018           0 :         libmesh_error_msg(
    1019             :             "We do not currently support static condensation of the diagonal matrix type");
    1020           0 :       matrix = std::make_unique<StaticCondensation>(this->get_mesh(),
    1021             :                                                     *this,
    1022             :                                                     this->get_dof_map(),
    1023           0 :                                                     this->get_dof_map().get_static_condensation());
    1024             :     }
    1025             :   else
    1026       40182 :     matrix = SparseMatrix<Number>::build(this->comm(), libMesh::default_solver_package());
    1027         566 :   auto & mat = *matrix;
    1028             : 
    1029         566 :   _matrices.emplace(mat_name, std::move(matrix));
    1030             : 
    1031         566 :   _matrix_types.emplace(mat_name, type);
    1032             : 
    1033             :   // Initialize it first if we've already initialized the others.
    1034       20374 :   this->late_matrix_init(mat, type);
    1035             : 
    1036         566 :   return mat;
    1037       19242 : }
    1038             : 
    1039             : 
    1040             : 
    1041         630 : SparseMatrix<Number> & System::add_matrix (std::string_view mat_name,
    1042             :                                            std::unique_ptr<SparseMatrix<Number>> matrix,
    1043             :                                            const ParallelType type)
    1044             : {
    1045          18 :   parallel_object_only();
    1046             : 
    1047         612 :   const std::string namestr{mat_name};
    1048             : 
    1049          18 :   libmesh_assert(this->comm().verify(namestr));
    1050          18 :   libmesh_assert(this->comm().verify(int(type)));
    1051             : 
    1052          18 :   SparseMatrix<Number> & mat = *matrix;
    1053             : 
    1054         630 :   _matrices[namestr] = std::move(matrix);
    1055         630 :   _matrix_types[namestr] = type;
    1056             : 
    1057             :   // Initialize it first if we've already initialized the others.
    1058         630 :   this->late_matrix_init(mat, type);
    1059             : 
    1060         648 :   return mat;
    1061             : }
    1062             : 
    1063       21004 : void System::late_matrix_init(SparseMatrix<Number> & mat,
    1064             :                               ParallelType type)
    1065             : {
    1066       21004 :   if (_matrices_initialized)
    1067             :     {
    1068           0 :       this->get_dof_map().attach_matrix(mat);
    1069           0 :       mat.init(type);
    1070             :     }
    1071       21004 : }
    1072             : 
    1073             : 
    1074             : 
    1075             : 
    1076           0 : void System::remove_matrix (std::string_view mat_name)
    1077             : {
    1078           0 :   parallel_object_only();  // Not strictly needed, but the only safe way to keep in sync
    1079             : 
    1080           0 :   if (const auto pos = _matrices.find(mat_name);
    1081           0 :       pos != _matrices.end())
    1082           0 :     _matrices.erase(pos); // erase()'d entries are destroyed
    1083           0 : }
    1084             : 
    1085             : 
    1086             : 
    1087          14 : const SparseMatrix<Number> * System::request_matrix (std::string_view mat_name) const
    1088             : {
    1089          14 :   if (const auto pos = _matrices.find(mat_name);
    1090          14 :       pos != _matrices.end())
    1091          14 :     return pos->second.get();
    1092             : 
    1093             :   // Otherwise, mat_name does not exist
    1094           0 :   return nullptr;
    1095             : }
    1096             : 
    1097             : 
    1098             : 
    1099      257233 : SparseMatrix<Number> * System::request_matrix (std::string_view mat_name)
    1100             : {
    1101      257233 :   if (auto pos = _matrices.find(mat_name);
    1102        7508 :       pos != _matrices.end())
    1103           2 :     return pos->second.get();
    1104             : 
    1105             :   // Otherwise, mat_name does not exist
    1106        7506 :   return nullptr;
    1107             : }
    1108             : 
    1109             : 
    1110             : 
    1111           0 : const SparseMatrix<Number> & System::get_matrix (std::string_view mat_name) const
    1112             : {
    1113           0 :   return *libmesh_map_find(_matrices, mat_name);
    1114             : }
    1115             : 
    1116             : 
    1117             : 
    1118      665417 : SparseMatrix<Number> & System::get_matrix (std::string_view mat_name)
    1119             : {
    1120      665417 :   return *libmesh_map_find(_matrices, mat_name);
    1121             : }
    1122             : 
    1123             : 
    1124             : 
    1125       69038 : void System::set_vector_preservation (const std::string & vec_name,
    1126             :                                       bool preserve)
    1127             : {
    1128        1972 :   parallel_object_only();  // Not strictly needed, but the only safe way to keep in sync
    1129             : 
    1130       69038 :   _vector_projections[vec_name] = preserve;
    1131       69038 : }
    1132             : 
    1133             : 
    1134             : 
    1135      159257 : bool System::vector_preservation (std::string_view vec_name) const
    1136             : {
    1137      159257 :   if (auto it = _vector_projections.find(vec_name);
    1138        4550 :       it != _vector_projections.end())
    1139      159257 :     return it->second;
    1140             : 
    1141             :   // vec_name was not in the map, return false
    1142           0 :   return false;
    1143             : }
    1144             : 
    1145             : 
    1146             : 
    1147       24746 : void System::set_vector_as_adjoint (const std::string & vec_name,
    1148             :                                     int qoi_num)
    1149             : {
    1150         798 :   parallel_object_only();  // Not strictly needed, but the only safe way to keep in sync
    1151             : 
    1152             :   // We reserve -1 for vectors which get primal constraints, -2 for
    1153             :   // vectors which get no constraints
    1154         798 :   libmesh_assert_greater_equal(qoi_num, -2);
    1155       24746 :   _vector_is_adjoint[vec_name] = qoi_num;
    1156       24746 : }
    1157             : 
    1158             : 
    1159             : 
    1160       27375 : int System::vector_is_adjoint (std::string_view vec_name) const
    1161             : {
    1162        1020 :   const auto it = _vector_is_adjoint.find(vec_name);
    1163        1020 :   libmesh_assert(it != _vector_is_adjoint.end());
    1164       27375 :   return it->second;
    1165             : }
    1166             : 
    1167             : 
    1168             : 
    1169         142 : NumericVector<Number> & System::add_sensitivity_solution (unsigned int i)
    1170             : {
    1171         142 :   std::ostringstream sensitivity_name;
    1172         138 :   sensitivity_name << "sensitivity_solution" << i;
    1173             : 
    1174         284 :   return this->add_vector(sensitivity_name.str());
    1175         134 : }
    1176             : 
    1177             : 
    1178             : 
    1179         284 : NumericVector<Number> & System::get_sensitivity_solution (unsigned int i)
    1180             : {
    1181         284 :   std::ostringstream sensitivity_name;
    1182         276 :   sensitivity_name << "sensitivity_solution" << i;
    1183             : 
    1184         568 :   return this->get_vector(sensitivity_name.str());
    1185         268 : }
    1186             : 
    1187             : 
    1188             : 
    1189           0 : const NumericVector<Number> & System::get_sensitivity_solution (unsigned int i) const
    1190             : {
    1191           0 :   std::ostringstream sensitivity_name;
    1192           0 :   sensitivity_name << "sensitivity_solution" << i;
    1193             : 
    1194           0 :   return this->get_vector(sensitivity_name.str());
    1195           0 : }
    1196             : 
    1197             : 
    1198             : 
    1199           0 : NumericVector<Number> & System::add_weighted_sensitivity_solution ()
    1200             : {
    1201           0 :   return this->add_vector("weighted_sensitivity_solution");
    1202             : }
    1203             : 
    1204             : 
    1205             : 
    1206           0 : NumericVector<Number> & System::get_weighted_sensitivity_solution ()
    1207             : {
    1208           0 :   return this->get_vector("weighted_sensitivity_solution");
    1209             : }
    1210             : 
    1211             : 
    1212             : 
    1213           0 : const NumericVector<Number> & System::get_weighted_sensitivity_solution () const
    1214             : {
    1215           0 :   return this->get_vector("weighted_sensitivity_solution");
    1216             : }
    1217             : 
    1218             : 
    1219             : 
    1220       24746 : NumericVector<Number> & System::add_adjoint_solution (unsigned int i)
    1221             : {
    1222       24746 :   std::ostringstream adjoint_name;
    1223       23948 :   adjoint_name << "adjoint_solution" << i;
    1224             : 
    1225       24746 :   NumericVector<Number> & returnval = this->add_vector(adjoint_name.str());
    1226       25544 :   this->set_vector_as_adjoint(adjoint_name.str(), i);
    1227       25544 :   return returnval;
    1228       23150 : }
    1229             : 
    1230             : 
    1231             : 
    1232     2885957 : NumericVector<Number> & System::get_adjoint_solution (unsigned int i)
    1233             : {
    1234     2885957 :   std::ostringstream adjoint_name;
    1235     2663375 :   adjoint_name << "adjoint_solution" << i;
    1236             : 
    1237     5771914 :   return this->get_vector(adjoint_name.str());
    1238     2440605 : }
    1239             : 
    1240             : 
    1241             : 
    1242        5792 : const NumericVector<Number> & System::get_adjoint_solution (unsigned int i) const
    1243             : {
    1244        5792 :   std::ostringstream adjoint_name;
    1245        5480 :   adjoint_name << "adjoint_solution" << i;
    1246             : 
    1247       11584 :   return this->get_vector(adjoint_name.str());
    1248        5168 : }
    1249             : 
    1250             : 
    1251             : 
    1252           0 : NumericVector<Number> & System::add_weighted_sensitivity_adjoint_solution (unsigned int i)
    1253             : {
    1254           0 :   std::ostringstream adjoint_name;
    1255           0 :   adjoint_name << "weighted_sensitivity_adjoint_solution" << i;
    1256             : 
    1257           0 :   NumericVector<Number> & returnval = this->add_vector(adjoint_name.str());
    1258           0 :   this->set_vector_as_adjoint(adjoint_name.str(), i);
    1259           0 :   return returnval;
    1260           0 : }
    1261             : 
    1262             : 
    1263             : 
    1264           0 : NumericVector<Number> & System::get_weighted_sensitivity_adjoint_solution (unsigned int i)
    1265             : {
    1266           0 :   std::ostringstream adjoint_name;
    1267           0 :   adjoint_name << "weighted_sensitivity_adjoint_solution" << i;
    1268             : 
    1269           0 :   return this->get_vector(adjoint_name.str());
    1270           0 : }
    1271             : 
    1272             : 
    1273             : 
    1274           0 : const NumericVector<Number> & System::get_weighted_sensitivity_adjoint_solution (unsigned int i) const
    1275             : {
    1276           0 :   std::ostringstream adjoint_name;
    1277           0 :   adjoint_name << "weighted_sensitivity_adjoint_solution" << i;
    1278             : 
    1279           0 :   return this->get_vector(adjoint_name.str());
    1280           0 : }
    1281             : 
    1282             : 
    1283             : 
    1284       24817 : NumericVector<Number> & System::add_adjoint_rhs (unsigned int i)
    1285             : {
    1286       24817 :   std::ostringstream adjoint_rhs_name;
    1287       24017 :   adjoint_rhs_name << "adjoint_rhs" << i;
    1288             : 
    1289       49634 :   return this->add_vector(adjoint_rhs_name.str(), false);
    1290       23217 : }
    1291             : 
    1292             : 
    1293             : 
    1294     2563081 : NumericVector<Number> & System::get_adjoint_rhs (unsigned int i)
    1295             : {
    1296     2563081 :   std::ostringstream adjoint_rhs_name;
    1297     2332959 :   adjoint_rhs_name << "adjoint_rhs" << i;
    1298             : 
    1299     5126162 :   return this->get_vector(adjoint_rhs_name.str());
    1300     2102825 : }
    1301             : 
    1302             : 
    1303             : 
    1304           0 : const NumericVector<Number> & System::get_adjoint_rhs (unsigned int i) const
    1305             : {
    1306           0 :   std::ostringstream adjoint_rhs_name;
    1307           0 :   adjoint_rhs_name << "adjoint_rhs" << i;
    1308             : 
    1309           0 :   return this->get_vector(adjoint_rhs_name.str());
    1310           0 : }
    1311             : 
    1312             : 
    1313             : 
    1314        7430 : NumericVector<Number> & System::add_sensitivity_rhs (unsigned int i)
    1315             : {
    1316        7430 :   std::ostringstream sensitivity_rhs_name;
    1317        7218 :   sensitivity_rhs_name << "sensitivity_rhs" << i;
    1318             : 
    1319       14860 :   return this->add_vector(sensitivity_rhs_name.str(), false);
    1320        7006 : }
    1321             : 
    1322             : 
    1323             : 
    1324        7430 : NumericVector<Number> & System::get_sensitivity_rhs (unsigned int i)
    1325             : {
    1326        7430 :   std::ostringstream sensitivity_rhs_name;
    1327        7218 :   sensitivity_rhs_name << "sensitivity_rhs" << i;
    1328             : 
    1329       14860 :   return this->get_vector(sensitivity_rhs_name.str());
    1330        7006 : }
    1331             : 
    1332             : 
    1333             : 
    1334           0 : const NumericVector<Number> & System::get_sensitivity_rhs (unsigned int i) const
    1335             : {
    1336           0 :   std::ostringstream sensitivity_rhs_name;
    1337           0 :   sensitivity_rhs_name << "sensitivity_rhs" << i;
    1338             : 
    1339           0 :   return this->get_vector(sensitivity_rhs_name.str());
    1340           0 : }
    1341             : 
    1342             : 
    1343             : 
    1344      273574 : unsigned int System::add_variable (std::string_view var,
    1345             :                                    const FEType & type,
    1346             :                                    const std::set<subdomain_id_type> * const active_subdomains)
    1347             : {
    1348      273574 :   return this->get_dof_map().add_variable(*this, var, type, active_subdomains);
    1349             : }
    1350             : 
    1351             : 
    1352             : 
    1353      268242 : unsigned int System::add_variable (std::string_view var,
    1354             :                                    const Order order,
    1355             :                                    const FEFamily family,
    1356             :                                    const std::set<subdomain_id_type> * const active_subdomains,
    1357             :                                    const bool p_refinement)
    1358             : {
    1359      268242 :   return this->add_variable(var,
    1360      275830 :                             FEType(order, family).set_p_refinement(p_refinement),
    1361      275830 :                             active_subdomains);
    1362             : }
    1363             : 
    1364             : 
    1365             : 
    1366          71 : unsigned int System::add_variables (const std::vector<std::string> & vars,
    1367             :                                     const FEType & type,
    1368             :                                     const std::set<subdomain_id_type> * const active_subdomains)
    1369             : {
    1370          71 :   return this->get_dof_map().add_variables(*this, vars, type, active_subdomains);
    1371             : }
    1372             : 
    1373             : 
    1374             : 
    1375          71 : unsigned int System::add_variables (const std::vector<std::string> & vars,
    1376             :                                     const Order order,
    1377             :                                     const FEFamily family,
    1378             :                                     const std::set<subdomain_id_type> * const active_subdomains,
    1379             :                                     const bool p_refinement)
    1380             : {
    1381          71 :   return this->add_variables(vars,
    1382          73 :                              FEType(order, family).set_p_refinement(p_refinement),
    1383          73 :                              active_subdomains);
    1384             : }
    1385             : 
    1386         568 : unsigned int System::add_variable_array (const std::vector<std::string> & vars,
    1387             :                                          const FEType & type,
    1388             :                                          const std::set<subdomain_id_type> * const active_subdomains)
    1389             : {
    1390         568 :   return this->get_dof_map().add_variable_array(*this, vars, type, active_subdomains);
    1391             : }
    1392             : 
    1393         894 : bool System::has_variable (std::string_view var) const
    1394             : {
    1395         894 :   return this->get_dof_map().has_variable(var);
    1396             : }
    1397             : 
    1398     8903978 : unsigned int System::variable_number (std::string_view var) const
    1399             : {
    1400     8903978 :   return this->get_dof_map().variable_number(var);
    1401             : }
    1402             : 
    1403         493 : void System::get_all_variable_numbers(std::vector<unsigned int> & all_variable_numbers) const
    1404             : {
    1405         493 :   this->get_dof_map().get_all_variable_numbers(all_variable_numbers);
    1406         493 : }
    1407             : 
    1408             : 
    1409         142 : void System::local_dof_indices(const unsigned int var,
    1410             :                                std::set<dof_id_type> & var_indices) const
    1411             : {
    1412             :   // Make sure the set is clear
    1413           4 :   var_indices.clear();
    1414             : 
    1415           8 :   std::vector<dof_id_type> dof_indices;
    1416             : 
    1417             :   const dof_id_type
    1418           4 :     first_local = this->get_dof_map().first_dof(),
    1419           4 :     end_local   = this->get_dof_map().end_dof();
    1420             : 
    1421             :   // Begin the loop over the elements
    1422         984 :   for (const auto & elem : this->get_mesh().active_local_element_ptr_range())
    1423             :     {
    1424         384 :       this->get_dof_map().dof_indices (elem, dof_indices, var);
    1425             : 
    1426        1152 :       for (dof_id_type dof : dof_indices)
    1427             :         //If the dof is owned by the local processor
    1428         768 :         if (first_local <= dof && dof < end_local)
    1429         503 :           var_indices.insert(dof);
    1430         134 :     }
    1431             : 
    1432             :   // we may have missed assigning DOFs to nodes that we own
    1433             :   // but to which we have no connected elements matching our
    1434             :   // variable restriction criterion.  this will happen, for example,
    1435             :   // if variable V is restricted to subdomain S.  We may not own
    1436             :   // any elements which live in S, but we may own nodes which are
    1437             :   // *connected* to elements which do.
    1438        1380 :   for (const auto & node : this->get_mesh().local_node_ptr_range())
    1439             :     {
    1440          50 :       libmesh_assert(node);
    1441         600 :       this->get_dof_map().dof_indices (node, dof_indices, var);
    1442         960 :       for (auto dof : dof_indices)
    1443         360 :         if (first_local <= dof && dof < end_local)
    1444         330 :           var_indices.insert(dof);
    1445         134 :     }
    1446         142 : }
    1447             : 
    1448             : 
    1449             : 
    1450           0 : void System::zero_variable (NumericVector<Number> & v,
    1451             :                             unsigned int var_num) const
    1452             : {
    1453             :   /* Make sure the call makes sense.  */
    1454           0 :   libmesh_assert_less (var_num, this->n_vars());
    1455             : 
    1456             :   /* Get a reference to the mesh.  */
    1457           0 :   const MeshBase & mesh = this->get_mesh();
    1458             : 
    1459             :   /* Check which system we are.  */
    1460           0 :   const unsigned int sys_num = this->number();
    1461             : 
    1462             :   // Loop over nodes.
    1463           0 :   for (const auto & node : mesh.local_node_ptr_range())
    1464             :     {
    1465           0 :       unsigned int n_comp = node->n_comp(sys_num,var_num);
    1466           0 :       for (unsigned int i=0; i<n_comp; i++)
    1467             :         {
    1468           0 :           const dof_id_type index = node->dof_number(sys_num,var_num,i);
    1469           0 :           v.set(index,0.0);
    1470             :         }
    1471           0 :     }
    1472             : 
    1473             :   // Loop over elements.
    1474             :   Threads::parallel_for
    1475           0 :     (mesh.active_local_element_stored_range(),
    1476           0 :      [sys_num, var_num, &v](const ConstElemRange & range)
    1477             :      {
    1478           0 :        for (const Elem * elem : range)
    1479             :          {
    1480           0 :            unsigned int n_comp = elem->n_comp(sys_num,var_num);
    1481           0 :            for (unsigned int i=0; i<n_comp; i++)
    1482             :              {
    1483           0 :                const dof_id_type index = elem->dof_number(sys_num,var_num,i);
    1484           0 :                v.set(index,0.0);
    1485             :              }
    1486             :          }
    1487           0 :      });
    1488           0 : }
    1489             : 
    1490             : 
    1491             : 
    1492           0 : Real System::discrete_var_norm(const NumericVector<Number> & v,
    1493             :                                unsigned int var,
    1494             :                                FEMNormType norm_type) const
    1495             : {
    1496           0 :   std::set<dof_id_type> var_indices;
    1497           0 :   local_dof_indices(var, var_indices);
    1498             : 
    1499           0 :   if (norm_type == DISCRETE_L1)
    1500           0 :     return v.subset_l1_norm(var_indices);
    1501           0 :   if (norm_type == DISCRETE_L2)
    1502           0 :     return v.subset_l2_norm(var_indices);
    1503           0 :   if (norm_type == DISCRETE_L_INF)
    1504           0 :     return v.subset_linfty_norm(var_indices);
    1505             :   else
    1506           0 :     libmesh_error_msg("Invalid norm_type = " << Utility::enum_to_string(norm_type));
    1507             : }
    1508             : 
    1509             : 
    1510             : 
    1511      106884 : Real System::calculate_norm(const NumericVector<Number> & v,
    1512             :                             unsigned int var,
    1513             :                             FEMNormType norm_type,
    1514             :                             std::set<unsigned int> * skip_dimensions) const
    1515             : {
    1516             :   //short circuit to save time
    1517      106884 :   if (norm_type == DISCRETE_L1 ||
    1518      106884 :       norm_type == DISCRETE_L2 ||
    1519             :       norm_type == DISCRETE_L_INF)
    1520           0 :     return discrete_var_norm(v,var,norm_type);
    1521             : 
    1522             :   // Not a discrete norm
    1523      110118 :   std::vector<FEMNormType> norms(this->n_vars(), L2);
    1524      106884 :   std::vector<Real> weights(this->n_vars(), 0.0);
    1525      106884 :   norms[var] = norm_type;
    1526      106884 :   weights[var] = 1.0;
    1527      207488 :   Real val = this->calculate_norm(v, SystemNorm(norms, weights), skip_dimensions);
    1528        3234 :   return val;
    1529             : }
    1530             : 
    1531             : 
    1532             : 
    1533      133121 : Real System::calculate_norm(const NumericVector<Number> & v,
    1534             :                             const SystemNorm & norm,
    1535             :                             std::set<unsigned int> * skip_dimensions) const
    1536             : {
    1537             :   // This function must be run on all processors at once
    1538        4172 :   parallel_object_only();
    1539             : 
    1540        8344 :   LOG_SCOPE ("calculate_norm()", "System");
    1541             : 
    1542             :   // Zero the norm before summation
    1543      133121 :   Real v_norm = 0.;
    1544             : 
    1545      133121 :   if (norm.is_discrete())
    1546             :     {
    1547             :       //Check to see if all weights are 1.0 and all types are equal
    1548       11340 :       FEMNormType norm_type0 = norm.type(0);
    1549       11340 :       unsigned int check_var = 0, check_end = this->n_vars();
    1550       22680 :       for (; check_var != check_end; ++check_var)
    1551       11340 :         if ((norm.weight(check_var) != 1.0) || (norm.type(check_var) != norm_type0))
    1552           0 :           break;
    1553             : 
    1554             :       //All weights were 1.0 so just do the full vector discrete norm
    1555       11340 :       if (check_var == this->n_vars())
    1556             :         {
    1557       11340 :           if (norm_type0 == DISCRETE_L1)
    1558           0 :             return v.l1_norm();
    1559       11340 :           if (norm_type0 == DISCRETE_L2)
    1560       11340 :             return v.l2_norm();
    1561           0 :           if (norm_type0 == DISCRETE_L_INF)
    1562           0 :             return v.linfty_norm();
    1563             :           else
    1564           0 :             libmesh_error_msg("Invalid norm_type0 = " << Utility::enum_to_string(norm_type0));
    1565             :         }
    1566             : 
    1567           0 :       for (auto var : make_range(this->n_vars()))
    1568             :         {
    1569             :           // Skip any variables we don't need to integrate
    1570           0 :           if (norm.weight(var) == 0.0)
    1571           0 :             continue;
    1572             : 
    1573           0 :           v_norm += norm.weight(var) * discrete_var_norm(v, var, norm.type(var));
    1574             :         }
    1575             : 
    1576           0 :       return v_norm;
    1577             :     }
    1578             : 
    1579             :   // Localize the potentially parallel vector
    1580      121781 :   std::unique_ptr<NumericVector<Number>> local_v = NumericVector<Number>::build(this->comm());
    1581      125629 :   local_v->init(v.size(), v.local_size(), _dof_map->get_send_list(),
    1582        7508 :                 true, GHOSTED);
    1583      125441 :   v.localize (*local_v, _dof_map->get_send_list());
    1584             : 
    1585             :   // I'm not sure how best to mix Hilbert norms on some variables (for
    1586             :   // which we'll want to square then sum then square root) with norms
    1587             :   // like L_inf (for which we'll just want to take an absolute value
    1588             :   // and then sum).
    1589        3848 :   bool using_hilbert_norm = true,
    1590        3848 :     using_nonhilbert_norm = true;
    1591             : 
    1592             :   // Loop over all variables
    1593      243562 :   for (auto var : make_range(this->n_vars()))
    1594             :     {
    1595             :       // Skip any variables we don't need to integrate
    1596      121781 :       Real norm_weight_sq = norm.weight_sq(var);
    1597      121781 :       if (norm_weight_sq == 0.0)
    1598           0 :         continue;
    1599      121781 :       Real norm_weight = norm.weight(var);
    1600             : 
    1601             :       // Check for unimplemented norms (rather than just returning 0).
    1602      121781 :       FEMNormType norm_type = norm.type(var);
    1603      121781 :       if ((norm_type==H1) ||
    1604      117977 :           (norm_type==H2) ||
    1605      117957 :           (norm_type==L2) ||
    1606      117949 :           (norm_type==H1_SEMINORM) ||
    1607             :           (norm_type==H2_SEMINORM))
    1608             :         {
    1609      121181 :           if (!using_hilbert_norm)
    1610           0 :             libmesh_not_implemented();
    1611        3832 :           using_nonhilbert_norm = false;
    1612             :         }
    1613         616 :       else if ((norm_type==L1) ||
    1614         592 :                (norm_type==L_INF) ||
    1615         584 :                (norm_type==W1_INF_SEMINORM) ||
    1616             :                (norm_type==W2_INF_SEMINORM))
    1617             :         {
    1618         600 :           if (!using_nonhilbert_norm)
    1619           0 :             libmesh_not_implemented();
    1620          16 :           using_hilbert_norm = false;
    1621             :         }
    1622             :       else
    1623           0 :         libmesh_not_implemented();
    1624             : 
    1625        3848 :       const FEType & fe_type = this->get_dof_map().variable_type(var);
    1626             : 
    1627             :       // Allow space for dims 0-3, and for both scalar and vector
    1628             :       // elements, even if we don't use them all
    1629      129289 :       std::vector<std::unique_ptr<FEBase>> fe_ptrs(4);
    1630      129289 :       std::vector<std::unique_ptr<FEVectorBase>> vec_fe_ptrs(4);
    1631      129289 :       std::vector<std::unique_ptr<QBase>> q_rules(4);
    1632             : 
    1633      121781 :       const std::set<unsigned char> & elem_dims = _mesh.elem_dimensions();
    1634             : 
    1635             :       // Prepare finite elements for each dimension present in the mesh
    1636      244972 :       for (const auto & dim : elem_dims)
    1637             :         {
    1638      123267 :           if (skip_dimensions && skip_dimensions->find(dim) != skip_dimensions->end())
    1639        1372 :             continue;
    1640             : 
    1641             :           // Construct quadrature and finite element objects
    1642      121781 :           q_rules[dim] = fe_type.default_quadrature_rule (dim);
    1643             : 
    1644      121781 :           const FEFieldType field_type = FEInterface::field_type(fe_type);
    1645      121781 :           if (field_type == TYPE_SCALAR)
    1646             :             {
    1647      121355 :               fe_ptrs[dim] = FEBase::build(dim, fe_type);
    1648      128651 :               fe_ptrs[dim]->attach_quadrature_rule (q_rules[dim].get());
    1649             :             }
    1650             :           else
    1651             :             {
    1652         426 :               vec_fe_ptrs[dim] = FEVectorBase::build(dim, fe_type);
    1653         450 :               vec_fe_ptrs[dim]->attach_quadrature_rule (q_rules[dim].get());
    1654          12 :               libmesh_assert_equal_to(field_type, TYPE_VECTOR);
    1655             :             }
    1656             : 
    1657             :         }
    1658             : 
    1659        7696 :       std::vector<dof_id_type> dof_indices;
    1660             : 
    1661             :       // Begin the loop over the elements
    1662    31907456 :       for (const auto & elem : this->get_mesh().active_local_element_ptr_range())
    1663             :         {
    1664    17489993 :           const unsigned int dim = elem->dim();
    1665             : 
    1666             :           // One way for implementing this would be to exchange the fe with the FEInterface- class.
    1667             :           // However, it needs to be discussed whether integral-norms make sense for infinite elements.
    1668             :           // or in which sense they could make sense.
    1669     3519431 :           if (elem->infinite() )
    1670           0 :             libmesh_not_implemented();
    1671             : 
    1672    17493791 :           if (skip_dimensions && skip_dimensions->find(dim) != skip_dimensions->end())
    1673       25062 :             continue;
    1674             : 
    1675    19090199 :           QBase * qrule = q_rules[dim].get();
    1676     1682932 :           libmesh_assert(qrule);
    1677             : 
    1678    17464931 :           this->get_dof_map().dof_indices (elem, dof_indices, var);
    1679             : 
    1680    14156731 :           auto element_calculation = [&dof_indices, &elem,
    1681             :                norm_type, norm_weight, norm_weight_sq, &qrule,
    1682   953826418 :                &local_v, &v_norm](auto & fe) {
    1683             :           typedef typename std::remove_reference<decltype(fe)>::type::OutputShape OutputShape;
    1684             :           typedef typename TensorTools::MakeNumber<OutputShape>::type OutputNumberShape;
    1685             :           typedef typename std::remove_reference<decltype(fe)>::type::OutputGradient OutputGradient;
    1686             :           typedef typename TensorTools::MakeNumber<OutputGradient>::type OutputNumberGradient;
    1687             : 
    1688    17464931 :           const std::vector<Real> &                     JxW = fe.get_JxW();
    1689     1682932 :           const std::vector<std::vector<OutputShape>> * phi = nullptr;
    1690    17464931 :           if (norm_type == H1 ||
    1691    15784208 :               norm_type == H2 ||
    1692    15782827 :               norm_type == L2 ||
    1693    15782827 :               norm_type == L1 ||
    1694             :               norm_type == L_INF)
    1695     1682380 :             phi = &(fe.get_phi());
    1696             : 
    1697     1682932 :           const std::vector<std::vector<OutputGradient>> * dphi = nullptr;
    1698    19090199 :           if (norm_type == H1 ||
    1699    15784208 :               norm_type == H2 ||
    1700    15783932 :               norm_type == H1_SEMINORM ||
    1701             :               norm_type == W1_INF_SEMINORM)
    1702     1681275 :             dphi = &(fe.get_dphi());
    1703             : 
    1704             : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
    1705             :           typedef typename std::remove_reference<decltype(fe)>::type::OutputTensor OutputTensor;
    1706             : 
    1707     1682932 :           const std::vector<std::vector<OutputTensor>> *  d2phi = nullptr;
    1708    19090199 :           if (norm_type == H2 ||
    1709    17464931 :               norm_type == H2_SEMINORM ||
    1710             :               norm_type == W2_INF_SEMINORM)
    1711           0 :             d2phi = &(fe.get_d2phi());
    1712             : #endif
    1713             : 
    1714    17464931 :           fe.reinit (elem);
    1715             : 
    1716    17464931 :           const unsigned int n_qp = qrule->n_points();
    1717             : 
    1718             :           const unsigned int n_sf = cast_int<unsigned int>
    1719     3308200 :             (dof_indices.size());
    1720             : 
    1721             :           // Begin the loop over the Quadrature points.
    1722    91185407 :           for (unsigned int qp=0; qp<n_qp; qp++)
    1723             :             {
    1724    73720476 :               if (norm_type == L1)
    1725             :                 {
    1726           0 :                   OutputNumberShape u_h = 0.;
    1727           0 :                   for (unsigned int i=0; i != n_sf; ++i)
    1728           0 :                     u_h += (*phi)[i][qp] * (*local_v)(dof_indices[i]);
    1729           0 :                   v_norm += norm_weight *
    1730           0 :                     JxW[qp] * TensorTools::norm(u_h);
    1731             :                 }
    1732             : 
    1733    73720476 :               if (norm_type == L_INF)
    1734             :                 {
    1735        2772 :                   OutputNumberShape u_h = 0.;
    1736      440028 :                   for (unsigned int i=0; i != n_sf; ++i)
    1737      537030 :                     u_h += (*phi)[i][qp] * (*local_v)(dof_indices[i]);
    1738       34529 :                   v_norm = std::max(v_norm, norm_weight * TensorTools::norm(u_h));
    1739             :                 }
    1740             : 
    1741    73720476 :               if (norm_type == H1 ||
    1742    66685908 :                   norm_type == H2 ||
    1743             :                   norm_type == L2)
    1744             :                 {
    1745     7051411 :                   OutputNumberShape u_h = 0.;
    1746   420041952 :                   for (unsigned int i=0; i != n_sf; ++i)
    1747   471846076 :                     u_h += (*phi)[i][qp] * (*local_v)(dof_indices[i]);
    1748    80439549 :                   v_norm += norm_weight_sq *
    1749    73618794 :                     JxW[qp] * TensorTools::norm_sq(u_h);
    1750             :                 }
    1751             : 
    1752    80549547 :               if (norm_type == H1 ||
    1753    66685908 :                   norm_type == H2 ||
    1754    66660749 :                   norm_type == H1_SEMINORM)
    1755             :                 {
    1756     7037340 :                   OutputNumberGradient grad_u_h;
    1757   413874756 :                   for (unsigned int i=0; i != n_sf; ++i)
    1758   371655578 :                     grad_u_h.add_scaled((*dphi)[i][qp], (*local_v)(dof_indices[i]));
    1759    80256626 :                   v_norm += norm_weight_sq *
    1760    73449942 :                     JxW[qp] * grad_u_h.norm_sq();
    1761             :                 }
    1762             : 
    1763    73720476 :               if (norm_type == W1_INF_SEMINORM)
    1764             :                 {
    1765        2772 :                   OutputNumberGradient grad_u_h;
    1766      440028 :                   for (unsigned int i=0; i != n_sf; ++i)
    1767      438858 :                     grad_u_h.add_scaled((*dphi)[i][qp], (*local_v)(dof_indices[i]));
    1768       36441 :                   v_norm = std::max(v_norm, norm_weight * grad_u_h.norm());
    1769             :                 }
    1770             : 
    1771             : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
    1772             :               typedef typename TensorTools::MakeNumber<OutputTensor>::type OutputNumberTensor;
    1773             : 
    1774    80549547 :               if (norm_type == H2 ||
    1775    66660749 :                   norm_type == H2_SEMINORM)
    1776             :                 {
    1777           0 :                   OutputNumberTensor hess_u_h;
    1778           0 :                   for (unsigned int i=0; i != n_sf; ++i)
    1779           0 :                     hess_u_h.add_scaled((*d2phi)[i][qp], (*local_v)(dof_indices[i]));
    1780           0 :                   v_norm += norm_weight_sq *
    1781           0 :                     JxW[qp] * hess_u_h.norm_sq();
    1782             :                 }
    1783             : 
    1784    73720476 :               if (norm_type == W2_INF_SEMINORM)
    1785             :                 {
    1786           0 :                   OutputNumberTensor hess_u_h;
    1787           0 :                   for (unsigned int i=0; i != n_sf; ++i)
    1788           0 :                     hess_u_h.add_scaled((*d2phi)[i][qp], (*local_v)(dof_indices[i]));
    1789           0 :                   v_norm = std::max(v_norm, norm_weight * hess_u_h.norm());
    1790             :                 }
    1791             : #endif
    1792             :             }
    1793    34929862 :           };
    1794             : 
    1795     3308200 :           FEBase * scalar_fe = fe_ptrs[dim].get();
    1796     3308200 :           FEVectorBase * vec_fe = vec_fe_ptrs[dim].get();
    1797             : 
    1798    17464931 :           if (scalar_fe)
    1799             :             {
    1800     1681827 :               libmesh_assert(!vec_fe);
    1801    17451671 :               element_calculation(*scalar_fe);
    1802             :             }
    1803             : 
    1804    17464931 :           if (vec_fe)
    1805             :             {
    1806        1105 :               libmesh_assert(!scalar_fe);
    1807       13260 :               element_calculation(*vec_fe);
    1808             :             }
    1809      114273 :         }
    1810      114273 :     }
    1811             : 
    1812      121781 :   if (using_hilbert_norm)
    1813             :     {
    1814      121181 :       this->comm().sum(v_norm);
    1815      121181 :       v_norm = std::sqrt(v_norm);
    1816             :     }
    1817             :   else
    1818             :     {
    1819         600 :       this->comm().max(v_norm);
    1820             :     }
    1821             : 
    1822      121781 :   return v_norm;
    1823      114273 : }
    1824             : 
    1825             : 
    1826             : 
    1827       17958 : std::string System::get_info() const
    1828             : {
    1829       18954 :   std::ostringstream oss;
    1830             : 
    1831             : 
    1832         498 :   const std::string & sys_name = this->name();
    1833             : 
    1834       17958 :   oss << "   System #"  << this->number() << ", \"" << sys_name << "\"\n"
    1835       17958 :       << "    Type \""  << this->system_type() << "\"\n"
    1836       51882 :       << "    Variables=";
    1837             : 
    1838       58240 :   for (auto vg : make_range(this->n_variable_groups()))
    1839             :     {
    1840       23320 :       const VariableGroup & vg_description (this->variable_group(vg));
    1841             : 
    1842       23320 :       if (vg_description.n_variables() > 1) oss << "{ ";
    1843       57743 :       for (auto vn : make_range(vg_description.n_variables()))
    1844       66918 :         oss << "\"" << vg_description.name(vn) << "\" ";
    1845       23320 :       if (vg_description.n_variables() > 1) oss << "} ";
    1846             :     }
    1847             : 
    1848       17958 :   oss << '\n';
    1849             : 
    1850       17958 :   oss << "    Finite Element Types=";
    1851             : #ifndef LIBMESH_ENABLE_INFINITE_ELEMENTS
    1852       55335 :   for (auto vg : make_range(this->n_variable_groups()))
    1853             :     oss << "\""
    1854       43578 :         << Utility::enum_to_string<FEFamily>(this->get_dof_map().variable_group(vg).type().family)
    1855       43578 :         << "\" ";
    1856             : #else
    1857        2905 :   for (auto vg : make_range(this->n_variable_groups()))
    1858             :     {
    1859             :       oss << "\""
    1860        3062 :           << Utility::enum_to_string<FEFamily>(this->get_dof_map().variable_group(vg).type().family)
    1861             :           << "\", \""
    1862        3062 :           << Utility::enum_to_string<FEFamily>(this->get_dof_map().variable_group(vg).type().radial_family)
    1863        3293 :           << "\" ";
    1864             :     }
    1865             : 
    1866        1185 :   oss << '\n' << "    Infinite Element Mapping=";
    1867        2905 :   for (auto vg : make_range(this->n_variable_groups()))
    1868             :     oss << "\""
    1869        3062 :         << Utility::enum_to_string<InfMapType>(this->get_dof_map().variable_group(vg).type().inf_map)
    1870        2412 :         << "\" ";
    1871             : #endif
    1872             : 
    1873       17958 :   oss << '\n';
    1874             : 
    1875       17958 :   oss << "    Approximation Orders=";
    1876       58240 :   for (auto vg : make_range(this->n_variable_groups()))
    1877             :     {
    1878             : #ifndef LIBMESH_ENABLE_INFINITE_ELEMENTS
    1879             :       oss << "\""
    1880       43578 :           << Utility::enum_to_string<Order>(this->get_dof_map().variable_group(vg).type().order)
    1881       43578 :           << "\" ";
    1882             : #else
    1883             :       oss << "\""
    1884        3062 :           << Utility::enum_to_string<Order>(this->get_dof_map().variable_group(vg).type().order)
    1885             :           << "\", \""
    1886        3062 :           << Utility::enum_to_string<Order>(this->get_dof_map().variable_group(vg).type().radial_order)
    1887        3293 :           << "\" ";
    1888             : #endif
    1889             :     }
    1890             : 
    1891       17958 :   oss << '\n';
    1892             : 
    1893       17958 :   if (this->is_initialized())
    1894             :     {
    1895       34782 :       oss << "    n_dofs()="             << this->n_dofs()             << '\n';
    1896       17887 :       dof_id_type local_dofs = this->n_local_dofs();
    1897       17887 :       oss << "    n_local_dofs()="       << local_dofs                 << '\n';
    1898       17887 :       this->comm().max(local_dofs);
    1899       17887 :       oss << "    max(n_local_dofs())="  << local_dofs                 << '\n';
    1900             : #ifdef LIBMESH_ENABLE_CONSTRAINTS
    1901       17887 :       if (this->n_constrained_dofs())
    1902             :         {
    1903       19816 :           oss << "    n_constrained_dofs()=" << this->n_constrained_dofs() << '\n';
    1904       19530 :           oss << "    n_local_constrained_dofs()=" << this->n_local_constrained_dofs() << '\n';
    1905       10051 :           dof_id_type local_unconstrained_dofs = this->n_local_dofs() - this->n_local_constrained_dofs();
    1906       10051 :           this->comm().max(local_unconstrained_dofs);
    1907       10337 :           oss << "    max(local unconstrained dofs)=" << local_unconstrained_dofs << '\n';
    1908             :         }
    1909             : #endif
    1910       17887 :       if (this->has_static_condensation())
    1911         952 :         oss << "    n uncondensed dofs="
    1912        1008 :             << this->get_dof_map().get_static_condensation().n_dofs() << '\n';
    1913             :     }
    1914             :   else
    1915          71 :     oss << "    (still uninitialized)\n";
    1916             : 
    1917       34920 :   oss << "    " << "n_vectors()="  << this->n_vectors()  << '\n';
    1918       34920 :   oss << "    " << "n_matrices()="  << this->n_matrices()  << '\n';
    1919             :   //   oss << "    " << "n_additional_matrices()=" << this->n_additional_matrices() << '\n';
    1920             : 
    1921       35418 :   oss << this->get_dof_map().get_info();
    1922             : 
    1923       18456 :   return oss.str();
    1924       16962 : }
    1925             : 
    1926             : 
    1927             : 
    1928         430 : void System::attach_init_function (void fptr(EquationSystems & es,
    1929             :                                              const std::string & name))
    1930             : {
    1931          14 :   libmesh_assert(fptr);
    1932             : 
    1933         430 :   if (_init_system_object != nullptr)
    1934             :     {
    1935             :       libmesh_warning("WARNING:  Cannot specify both initialization function and object!");
    1936             : 
    1937           0 :       _init_system_object = nullptr;
    1938             :     }
    1939             : 
    1940         430 :   _init_system_function = fptr;
    1941         430 : }
    1942             : 
    1943             : 
    1944             : 
    1945         280 : void System::attach_init_object (System::Initialization & init_in)
    1946             : {
    1947         280 :   if (_init_system_function != nullptr)
    1948             :     {
    1949             :       libmesh_warning("WARNING:  Cannot specify both initialization object and function!");
    1950             : 
    1951           0 :       _init_system_function = nullptr;
    1952             :     }
    1953             : 
    1954         280 :   _init_system_object = &init_in;
    1955         280 : }
    1956             : 
    1957             : 
    1958             : 
    1959        9986 : void System::attach_assemble_function (void fptr(EquationSystems & es,
    1960             :                                                  const std::string & name))
    1961             : {
    1962         278 :   libmesh_assert(fptr);
    1963             : 
    1964        9986 :   if (_assemble_system_object != nullptr)
    1965             :     {
    1966             :       libmesh_warning("WARNING:  Cannot specify both assembly function and object!");
    1967             : 
    1968           0 :       _assemble_system_object = nullptr;
    1969             :     }
    1970             : 
    1971        9986 :   _assemble_system_function = fptr;
    1972        9986 : }
    1973             : 
    1974             : 
    1975             : 
    1976         142 : void System::attach_assemble_object (System::Assembly & assemble_in)
    1977             : {
    1978         142 :   if (_assemble_system_function != nullptr)
    1979             :     {
    1980             :       libmesh_warning("WARNING:  Cannot specify both assembly object and function!");
    1981             : 
    1982           0 :       _assemble_system_function = nullptr;
    1983             :     }
    1984             : 
    1985         142 :   _assemble_system_object = &assemble_in;
    1986         142 : }
    1987             : 
    1988             : 
    1989             : 
    1990           0 : void System::attach_constraint_function(void fptr(EquationSystems & es,
    1991             :                                                   const std::string & name))
    1992             : {
    1993           0 :   libmesh_assert(fptr);
    1994             : 
    1995           0 :   if (_constrain_system_object != nullptr)
    1996             :     {
    1997             :       libmesh_warning("WARNING:  Cannot specify both constraint function and object!");
    1998             : 
    1999           0 :       _constrain_system_object = nullptr;
    2000             :     }
    2001             : 
    2002           0 :   _constrain_system_function = fptr;
    2003           0 : }
    2004             : 
    2005             : 
    2006             : 
    2007        2485 : void System::attach_constraint_object (System::Constraint & constrain)
    2008             : {
    2009        2485 :   if (_constrain_system_function != nullptr)
    2010             :     {
    2011             :       libmesh_warning("WARNING:  Cannot specify both constraint object and function!");
    2012             : 
    2013           0 :       _constrain_system_function = nullptr;
    2014             :     }
    2015             : 
    2016        2485 :   _constrain_system_object = &constrain;
    2017        2485 : }
    2018             : 
    2019           0 : bool System::has_constraint_object () const
    2020             : {
    2021           0 :   return _constrain_system_object != nullptr;
    2022             : }
    2023             : 
    2024           0 : System::Constraint& System::get_constraint_object ()
    2025             : {
    2026           0 :   libmesh_assert_msg(_constrain_system_object,"No constraint object available.");
    2027           0 :   return *_constrain_system_object;
    2028             : }
    2029             : 
    2030             : 
    2031             : 
    2032           0 : void System::attach_QOI_function(void fptr(EquationSystems &,
    2033             :                                            const std::string &,
    2034             :                                            const QoISet &))
    2035             : {
    2036           0 :   libmesh_assert(fptr);
    2037             : 
    2038           0 :   if (_qoi_evaluate_object != nullptr)
    2039             :     {
    2040             :       libmesh_warning("WARNING:  Cannot specify both QOI function and object!");
    2041             : 
    2042           0 :       _qoi_evaluate_object = nullptr;
    2043             :     }
    2044             : 
    2045           0 :   _qoi_evaluate_function = fptr;
    2046           0 : }
    2047             : 
    2048             : 
    2049             : 
    2050           0 : void System::attach_QOI_object (QOI & qoi_in)
    2051             : {
    2052           0 :   if (_qoi_evaluate_function != nullptr)
    2053             :     {
    2054             :       libmesh_warning("WARNING:  Cannot specify both QOI object and function!");
    2055             : 
    2056           0 :       _qoi_evaluate_function = nullptr;
    2057             :     }
    2058             : 
    2059           0 :   _qoi_evaluate_object = &qoi_in;
    2060           0 : }
    2061             : 
    2062             : 
    2063             : 
    2064           0 : void System::attach_QOI_derivative(void fptr(EquationSystems &, const std::string &,
    2065             :                                              const QoISet &, bool, bool))
    2066             : {
    2067           0 :   libmesh_assert(fptr);
    2068             : 
    2069           0 :   if (_qoi_evaluate_derivative_object != nullptr)
    2070             :     {
    2071             :       libmesh_warning("WARNING:  Cannot specify both QOI derivative function and object!");
    2072             : 
    2073           0 :       _qoi_evaluate_derivative_object = nullptr;
    2074             :     }
    2075             : 
    2076           0 :   _qoi_evaluate_derivative_function = fptr;
    2077           0 : }
    2078             : 
    2079             : 
    2080             : 
    2081           0 : void System::attach_QOI_derivative_object (QOIDerivative & qoi_derivative)
    2082             : {
    2083           0 :   if (_qoi_evaluate_derivative_function != nullptr)
    2084             :     {
    2085             :       libmesh_warning("WARNING:  Cannot specify both QOI derivative object and function!");
    2086             : 
    2087           0 :       _qoi_evaluate_derivative_function = nullptr;
    2088             :     }
    2089             : 
    2090           0 :   _qoi_evaluate_derivative_object = &qoi_derivative;
    2091           0 : }
    2092             : 
    2093             : 
    2094             : 
    2095      241875 : void System::user_initialization ()
    2096             : {
    2097             :   // Call the user-provided initialization function,
    2098             :   // if it was provided
    2099      241875 :   if (_init_system_function != nullptr)
    2100         501 :     this->_init_system_function (_equation_systems, this->name());
    2101             : 
    2102             :   // ...or the user-provided initialization object.
    2103      241374 :   else if (_init_system_object != nullptr)
    2104         280 :     this->_init_system_object->initialize();
    2105      241875 : }
    2106             : 
    2107             : 
    2108             : 
    2109       46331 : void System::user_assembly ()
    2110             : {
    2111             :   // Call the user-provided assembly function,
    2112             :   // if it was provided
    2113       46331 :   if (_assemble_system_function != nullptr)
    2114       46189 :     this->_assemble_system_function (_equation_systems, this->name());
    2115             : 
    2116             :   // ...or the user-provided assembly object.
    2117         142 :   else if (_assemble_system_object != nullptr)
    2118         142 :     this->_assemble_system_object->assemble();
    2119       46331 : }
    2120             : 
    2121             : 
    2122             : 
    2123      274280 : void System::user_constrain ()
    2124             : {
    2125             :   // Call the user-provided constraint function,
    2126             :   // if it was provided
    2127      274280 :   if (_constrain_system_function!= nullptr)
    2128           0 :     this->_constrain_system_function(_equation_systems, this->name());
    2129             : 
    2130             :   // ...or the user-provided constraint object.
    2131      274280 :   else if (_constrain_system_object != nullptr)
    2132        2485 :     this->_constrain_system_object->constrain();
    2133      274280 : }
    2134             : 
    2135             : 
    2136             : 
    2137           0 : void System::user_QOI (const QoISet & qoi_indices)
    2138             : {
    2139             :   // Call the user-provided quantity of interest function,
    2140             :   // if it was provided
    2141           0 :   if (_qoi_evaluate_function != nullptr)
    2142           0 :     this->_qoi_evaluate_function(_equation_systems, this->name(), qoi_indices);
    2143             : 
    2144             :   // ...or the user-provided QOI function object.
    2145           0 :   else if (_qoi_evaluate_object != nullptr)
    2146           0 :     this->_qoi_evaluate_object->qoi(qoi_indices);
    2147           0 : }
    2148             : 
    2149             : 
    2150             : 
    2151           0 : void System::user_QOI_derivative(const QoISet & qoi_indices,
    2152             :                                  bool include_liftfunc,
    2153             :                                  bool apply_constraints)
    2154             : {
    2155             :   // Call the user-provided quantity of interest derivative,
    2156             :   // if it was provided
    2157           0 :   if (_qoi_evaluate_derivative_function != nullptr)
    2158           0 :     this->_qoi_evaluate_derivative_function
    2159           0 :       (_equation_systems, this->name(), qoi_indices, include_liftfunc,
    2160             :        apply_constraints);
    2161             : 
    2162             :   // ...or the user-provided QOI derivative function object.
    2163           0 :   else if (_qoi_evaluate_derivative_object != nullptr)
    2164           0 :     this->_qoi_evaluate_derivative_object->qoi_derivative
    2165           0 :       (qoi_indices, include_liftfunc, apply_constraints);
    2166           0 : }
    2167             : 
    2168             : 
    2169        2272 : void System::init_qois(unsigned int n_qois)
    2170             : {
    2171        2272 :   _qoi.resize(n_qois);
    2172        2272 :   _qoi_error_estimates.resize(n_qois);
    2173        2272 : }
    2174             : 
    2175             : 
    2176      117555 : void System::set_qoi(unsigned int qoi_index, Number qoi_value)
    2177             : {
    2178        3358 :   libmesh_assert(qoi_index < _qoi.size());
    2179             : 
    2180      120913 :   _qoi[qoi_index] = qoi_value;
    2181      117555 : }
    2182             : 
    2183             : 
    2184       51380 : Number System::get_qoi_value(unsigned int qoi_index) const
    2185             : {
    2186        1468 :   libmesh_assert(qoi_index < _qoi.size());
    2187       52848 :   return _qoi[qoi_index];
    2188             : }
    2189             : 
    2190             : 
    2191       54015 : std::vector<Number> System::get_qoi_values() const
    2192             : {
    2193       54015 :   return this->_qoi;
    2194             : }
    2195             : 
    2196             : 
    2197       39155 : void System::set_qoi(std::vector<Number> new_qoi)
    2198             : {
    2199        1118 :   libmesh_assert_equal_to(this->_qoi.size(), new_qoi.size());
    2200       39155 :   this->_qoi = std::move(new_qoi);
    2201       39155 : }
    2202             : 
    2203             : 
    2204       22400 : void System::set_qoi_error_estimate(unsigned int qoi_index, Number qoi_error_estimate)
    2205             : {
    2206         640 :   libmesh_assert(qoi_index < _qoi_error_estimates.size());
    2207             : 
    2208       23040 :   _qoi_error_estimates[qoi_index] = qoi_error_estimate;
    2209       22400 : }
    2210             : 
    2211       22400 : Number System::get_qoi_error_estimate_value(unsigned int qoi_index) const
    2212             : {
    2213         640 :   libmesh_assert(qoi_index < _qoi_error_estimates.size());
    2214       23040 :   return _qoi_error_estimates[qoi_index];
    2215             : }
    2216             : 
    2217             : 
    2218             : 
    2219      339077 : Number System::point_value(unsigned int var,
    2220             :                            const Point & p,
    2221             :                            const bool insist_on_success,
    2222             :                            const NumericVector<Number> *sol) const
    2223             : {
    2224             :   // This function must be called on every processor; there's no
    2225             :   // telling where in the partition p falls.
    2226        9516 :   parallel_object_only();
    2227             : 
    2228             :   // And every processor had better agree about which point we're
    2229             :   // looking for
    2230             : #ifndef NDEBUG
    2231        9516 :   libmesh_assert(this->comm().verify(p(0)));
    2232             : #if LIBMESH_DIM > 1
    2233        9516 :   libmesh_assert(this->comm().verify(p(1)));
    2234             : #endif
    2235             : #if LIBMESH_DIM > 2
    2236        9516 :   libmesh_assert(this->comm().verify(p(2)));
    2237             : #endif
    2238             : #endif // NDEBUG
    2239             : 
    2240             :   // Get a reference to the mesh object associated with the system object that calls this function
    2241       19032 :   const MeshBase & mesh = this->get_mesh();
    2242             : 
    2243             :   // Use an existing PointLocator or create a new one
    2244      339077 :   std::unique_ptr<PointLocatorBase> locator_ptr = mesh.sub_point_locator();
    2245        9516 :   PointLocatorBase & locator = *locator_ptr;
    2246             : 
    2247      339077 :   if (!insist_on_success || !mesh.is_serial())
    2248      293498 :     locator.enable_out_of_mesh_mode();
    2249             : 
    2250             :   // Get a pointer to an element that contains p and allows us to
    2251             :   // evaluate var
    2252             :   const std::set<subdomain_id_type> & raw_subdomains =
    2253      339077 :     this->variable(var).active_subdomains();
    2254             :   const std::set<subdomain_id_type> * implicit_subdomains =
    2255      339077 :     raw_subdomains.empty() ? nullptr : &raw_subdomains;
    2256      339077 :   const Elem * e = locator(p, implicit_subdomains);
    2257             : 
    2258      339077 :   Number u = 0;
    2259             : 
    2260      339077 :   if (e && this->get_dof_map().is_evaluable(*e, var))
    2261      120171 :     u = point_value(var, p, *e, sol);
    2262             : 
    2263             :   // If I have an element containing p, then let's let everyone know
    2264             :   processor_id_type lowest_owner =
    2265      339077 :     (e && (e->processor_id() == this->processor_id())) ?
    2266      334319 :     this->processor_id() : this->n_processors();
    2267      339077 :   this->comm().min(lowest_owner);
    2268             : 
    2269             :   // Everybody should get their value from a processor that was able
    2270             :   // to compute it.
    2271             :   // If nobody admits owning the point, we have a problem.
    2272      348593 :   if (lowest_owner != this->n_processors())
    2273      329561 :     this->comm().broadcast(u, lowest_owner);
    2274             :   else
    2275           0 :     libmesh_assert(!insist_on_success);
    2276             : 
    2277      358109 :   return u;
    2278      320045 : }
    2279             : 
    2280      138552 : Number System::point_value(unsigned int var,
    2281             :                            const Point & p,
    2282             :                            const Elem & e,
    2283             :                            const NumericVector<Number> *sol) const
    2284             : {
    2285             :   // Ensuring that the given point is really in the element is an
    2286             :   // expensive assert, but as long as debugging is turned on we might
    2287             :   // as well try to catch a particularly nasty potential error
    2288        7642 :   libmesh_assert (e.contains_point(p));
    2289             : 
    2290      138552 :   if (!sol)
    2291        4980 :     sol = this->current_local_solution.get();
    2292             : 
    2293             :   // Get the dof map to get the proper indices for our computation
    2294        7642 :   const DofMap & dof_map = this->get_dof_map();
    2295             : 
    2296             :   // Make sure we can evaluate on this element.
    2297        7642 :   libmesh_assert (dof_map.is_evaluable(e, var));
    2298             : 
    2299             :   // Need dof_indices for phi[i][j]
    2300       15284 :   std::vector<dof_id_type> dof_indices;
    2301             : 
    2302             :   // Fill in the dof_indices for our element
    2303      138552 :   dof_map.dof_indices (&e, dof_indices, var);
    2304             : 
    2305             :   // Get the no of dofs associated with this point
    2306             :   const unsigned int num_dofs = cast_int<unsigned int>
    2307       15284 :     (dof_indices.size());
    2308             : 
    2309      138552 :   FEType fe_type = dof_map.variable_type(var);
    2310             : 
    2311             :   // Map the physical co-ordinates to the master co-ordinates
    2312      138552 :   Point coor = FEMap::inverse_map(e.dim(), &e, p);
    2313             : 
    2314             :   // get the shape function value via the FEInterface to also handle the case
    2315             :   // of infinite elements correctly, the shape function is not fe->phi().
    2316      146194 :   FEComputeData fe_data(this->get_equation_systems(), coor);
    2317      138552 :   FEInterface::compute_data(e.dim(), fe_type, &e, fe_data);
    2318             : 
    2319             :   // Get ready to accumulate a value
    2320        7642 :   Number u = 0;
    2321             : 
    2322     2538928 :   for (unsigned int l=0; l<num_dofs; l++)
    2323             :     {
    2324     2610602 :       u += fe_data.shape[l] * (*sol)(dof_indices[l]);
    2325             :     }
    2326             : 
    2327      146194 :   return u;
    2328      123268 : }
    2329             : 
    2330             : 
    2331             : 
    2332       18381 : Number System::point_value(unsigned int var, const Point & p, const Elem * e) const
    2333             : {
    2334         337 :   libmesh_assert(e);
    2335       18381 :   return this->point_value(var, p, *e);
    2336             : }
    2337             : 
    2338             : 
    2339             : 
    2340      123722 : Number System::point_value(unsigned int var, const Point & p, const NumericVector<Number> * sol) const
    2341             : {
    2342      123722 :   return this->point_value(var, p, true, sol);
    2343             : }
    2344             : 
    2345             : 
    2346             : 
    2347             : 
    2348           0 : Gradient System::point_gradient(unsigned int var,
    2349             :                                 const Point & p,
    2350             :                                 const bool insist_on_success,
    2351             :                                 const NumericVector<Number> *sol) const
    2352             : {
    2353             :   // This function must be called on every processor; there's no
    2354             :   // telling where in the partition p falls.
    2355           0 :   parallel_object_only();
    2356             : 
    2357             :   // And every processor had better agree about which point we're
    2358             :   // looking for
    2359             : #ifndef NDEBUG
    2360           0 :   libmesh_assert(this->comm().verify(p(0)));
    2361             : #if LIBMESH_DIM > 1
    2362           0 :   libmesh_assert(this->comm().verify(p(1)));
    2363             : #endif
    2364             : #if LIBMESH_DIM > 2
    2365           0 :   libmesh_assert(this->comm().verify(p(2)));
    2366             : #endif
    2367             : #endif // NDEBUG
    2368             : 
    2369             :   // Get a reference to the mesh object associated with the system object that calls this function
    2370           0 :   const MeshBase & mesh = this->get_mesh();
    2371             : 
    2372             :   // Use an existing PointLocator or create a new one
    2373           0 :   std::unique_ptr<PointLocatorBase> locator_ptr = mesh.sub_point_locator();
    2374           0 :   PointLocatorBase & locator = *locator_ptr;
    2375             : 
    2376           0 :   if (!insist_on_success || !mesh.is_serial())
    2377           0 :     locator.enable_out_of_mesh_mode();
    2378             : 
    2379             :   // Get a pointer to an element that contains p and allows us to
    2380             :   // evaluate var
    2381             :   const std::set<subdomain_id_type> & raw_subdomains =
    2382           0 :     this->variable(var).active_subdomains();
    2383             :   const std::set<subdomain_id_type> * implicit_subdomains =
    2384           0 :     raw_subdomains.empty() ? nullptr : &raw_subdomains;
    2385           0 :   const Elem * e = locator(p, implicit_subdomains);
    2386             : 
    2387           0 :   Gradient grad_u;
    2388             : 
    2389           0 :   if (e && this->get_dof_map().is_evaluable(*e, var))
    2390           0 :     grad_u = point_gradient(var, p, *e, sol);
    2391             : 
    2392             :   // If I have an element containing p, then let's let everyone know
    2393             :   processor_id_type lowest_owner =
    2394           0 :     (e && (e->processor_id() == this->processor_id())) ?
    2395           0 :     this->processor_id() : this->n_processors();
    2396           0 :   this->comm().min(lowest_owner);
    2397             : 
    2398             :   // Everybody should get their value from a processor that was able
    2399             :   // to compute it.
    2400             :   // If nobody admits owning the point, we may have a problem.
    2401           0 :   if (lowest_owner != this->n_processors())
    2402           0 :     this->comm().broadcast(grad_u, lowest_owner);
    2403             :   else
    2404           0 :     libmesh_assert(!insist_on_success);
    2405             : 
    2406           0 :   return grad_u;
    2407           0 : }
    2408             : 
    2409             : 
    2410           0 : Gradient System::point_gradient(unsigned int var,
    2411             :                                 const Point & p,
    2412             :                                 const Elem & e,
    2413             :                                 const NumericVector<Number> *sol) const
    2414             : {
    2415             :   // Ensuring that the given point is really in the element is an
    2416             :   // expensive assert, but as long as debugging is turned on we might
    2417             :   // as well try to catch a particularly nasty potential error
    2418           0 :   libmesh_assert (e.contains_point(p));
    2419             : 
    2420           0 :   if (!sol)
    2421           0 :     sol = this->current_local_solution.get();
    2422             : 
    2423             :   // Get the dof map to get the proper indices for our computation
    2424           0 :   const DofMap & dof_map = this->get_dof_map();
    2425             : 
    2426             :   // write the element dimension into a separate variable.
    2427           0 :   const unsigned int dim = e.dim();
    2428             : 
    2429             :   // Make sure we can evaluate on this element.
    2430           0 :   libmesh_assert (dof_map.is_evaluable(e, var));
    2431             : 
    2432             :   // Need dof_indices for phi[i][j]
    2433           0 :   std::vector<dof_id_type> dof_indices;
    2434             : 
    2435             :   // Fill in the dof_indices for our element
    2436           0 :   dof_map.dof_indices (&e, dof_indices, var);
    2437             : 
    2438             :   // Get the no of dofs associated with this point
    2439             :   const unsigned int num_dofs = cast_int<unsigned int>
    2440           0 :     (dof_indices.size());
    2441             : 
    2442           0 :   FEType fe_type = dof_map.variable_type(var);
    2443             : 
    2444             :   // Map the physical co-ordinates to the master co-ordinates
    2445           0 :   Point coor = FEMap::inverse_map(dim, &e, p);
    2446             : 
    2447             :   // get the shape function value via the FEInterface to also handle the case
    2448             :   // of infinite elements correctly, the shape function is not fe->phi().
    2449           0 :   FEComputeData fe_data(this->get_equation_systems(), coor);
    2450           0 :   fe_data.enable_derivative();
    2451           0 :   FEInterface::compute_data(dim, fe_type, &e, fe_data);
    2452             : 
    2453             :   // Get ready to accumulate a gradient
    2454           0 :   Gradient grad_u;
    2455             : 
    2456           0 :   for (unsigned int l=0; l<num_dofs; l++)
    2457             :     {
    2458             :       // Chartesian coordinates have always LIBMESH_DIM entries,
    2459             :       // local coordinates have as many coordinates as the element has.
    2460           0 :       for (std::size_t v=0; v<dim; v++)
    2461           0 :         for (std::size_t xyz=0; xyz<LIBMESH_DIM; xyz++)
    2462             :           {
    2463             :             // FIXME: this needs better syntax: It is matrix-vector multiplication.
    2464           0 :             grad_u(xyz) += fe_data.local_transform[v][xyz]
    2465           0 :               * fe_data.dshape[l](v)
    2466           0 :               * (*sol)(dof_indices[l]);
    2467             :           }
    2468             :     }
    2469             : 
    2470           0 :   return grad_u;
    2471           0 : }
    2472             : 
    2473             : 
    2474             : 
    2475           0 : Gradient System::point_gradient(unsigned int var, const Point & p, const Elem * e) const
    2476             : {
    2477           0 :   libmesh_assert(e);
    2478           0 :   return this->point_gradient(var, p, *e);
    2479             : }
    2480             : 
    2481             : 
    2482             : 
    2483           0 : Gradient System::point_gradient(unsigned int var, const Point & p, const NumericVector<Number> * sol) const
    2484             : {
    2485           0 :   return this->point_gradient(var, p, true, sol);
    2486             : }
    2487             : 
    2488             : 
    2489             : 
    2490             : // We can only accumulate a hessian with --enable-second
    2491             : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
    2492           0 : Tensor System::point_hessian(unsigned int var,
    2493             :                              const Point & p,
    2494             :                              const bool insist_on_success,
    2495             :                              const NumericVector<Number> *sol) const
    2496             : {
    2497             :   // This function must be called on every processor; there's no
    2498             :   // telling where in the partition p falls.
    2499           0 :   parallel_object_only();
    2500             : 
    2501             :   // And every processor had better agree about which point we're
    2502             :   // looking for
    2503             : #ifndef NDEBUG
    2504           0 :   libmesh_assert(this->comm().verify(p(0)));
    2505             : #if LIBMESH_DIM > 1
    2506           0 :   libmesh_assert(this->comm().verify(p(1)));
    2507             : #endif
    2508             : #if LIBMESH_DIM > 2
    2509           0 :   libmesh_assert(this->comm().verify(p(2)));
    2510             : #endif
    2511             : #endif // NDEBUG
    2512             : 
    2513             :   // Get a reference to the mesh object associated with the system object that calls this function
    2514           0 :   const MeshBase & mesh = this->get_mesh();
    2515             : 
    2516             :   // Use an existing PointLocator or create a new one
    2517           0 :   std::unique_ptr<PointLocatorBase> locator_ptr = mesh.sub_point_locator();
    2518           0 :   PointLocatorBase & locator = *locator_ptr;
    2519             : 
    2520           0 :   if (!insist_on_success || !mesh.is_serial())
    2521           0 :     locator.enable_out_of_mesh_mode();
    2522             : 
    2523             :   // Get a pointer to an element that contains p and allows us to
    2524             :   // evaluate var
    2525             :   const std::set<subdomain_id_type> & raw_subdomains =
    2526           0 :     this->variable(var).active_subdomains();
    2527             :   const std::set<subdomain_id_type> * implicit_subdomains =
    2528           0 :     raw_subdomains.empty() ? nullptr : &raw_subdomains;
    2529           0 :   const Elem * e = locator(p, implicit_subdomains);
    2530             : 
    2531           0 :   Tensor hess_u;
    2532             : 
    2533           0 :   if (e && this->get_dof_map().is_evaluable(*e, var))
    2534           0 :     hess_u = point_hessian(var, p, *e, sol);
    2535             : 
    2536             :   // If I have an element containing p, then let's let everyone know
    2537             :   processor_id_type lowest_owner =
    2538           0 :     (e && (e->processor_id() == this->processor_id())) ?
    2539           0 :     this->processor_id() : this->n_processors();
    2540           0 :   this->comm().min(lowest_owner);
    2541             : 
    2542             :   // Everybody should get their value from a processor that was able
    2543             :   // to compute it.
    2544             :   // If nobody admits owning the point, we may have a problem.
    2545           0 :   if (lowest_owner != this->n_processors())
    2546           0 :     this->comm().broadcast(hess_u, lowest_owner);
    2547             :   else
    2548           0 :     libmesh_assert(!insist_on_success);
    2549             : 
    2550           0 :   return hess_u;
    2551           0 : }
    2552             : 
    2553           0 : Tensor System::point_hessian(unsigned int var,
    2554             :                              const Point & p,
    2555             :                              const Elem & e,
    2556             :                              const NumericVector<Number> *sol) const
    2557             : {
    2558             :   // Ensuring that the given point is really in the element is an
    2559             :   // expensive assert, but as long as debugging is turned on we might
    2560             :   // as well try to catch a particularly nasty potential error
    2561           0 :   libmesh_assert (e.contains_point(p));
    2562             : 
    2563           0 :   if (!sol)
    2564           0 :     sol = this->current_local_solution.get();
    2565             : 
    2566           0 :   if (e.infinite())
    2567           0 :     libmesh_not_implemented();
    2568             : 
    2569             :   // Get the dof map to get the proper indices for our computation
    2570           0 :   const DofMap & dof_map = this->get_dof_map();
    2571             : 
    2572             :   // Make sure we can evaluate on this element.
    2573           0 :   libmesh_assert (dof_map.is_evaluable(e, var));
    2574             : 
    2575             :   // Need dof_indices for phi[i][j]
    2576           0 :   std::vector<dof_id_type> dof_indices;
    2577             : 
    2578             :   // Fill in the dof_indices for our element
    2579           0 :   dof_map.dof_indices (&e, dof_indices, var);
    2580             : 
    2581             :   // Get the no of dofs associated with this point
    2582             :   const unsigned int num_dofs = cast_int<unsigned int>
    2583           0 :     (dof_indices.size());
    2584             : 
    2585           0 :   FEType fe_type = dof_map.variable_type(var);
    2586             : 
    2587             :   // Build a FE again so we can calculate u(p)
    2588           0 :   std::unique_ptr<FEBase> fe (FEBase::build(e.dim(), fe_type));
    2589             : 
    2590             :   // Map the physical co-ordinates to the master co-ordinates
    2591             :   // Build a vector of point co-ordinates to send to reinit
    2592           0 :   std::vector<Point> coor(1, FEMap::inverse_map(e.dim(), &e, p));
    2593             : 
    2594             :   // Get the values of the shape function derivatives
    2595           0 :   const std::vector<std::vector<RealTensor>> &  d2phi = fe->get_d2phi();
    2596             : 
    2597             :   // Reinitialize the element and compute the shape function values at coor
    2598           0 :   fe->reinit (&e, &coor);
    2599             : 
    2600             :   // Get ready to accumulate a hessian
    2601           0 :   Tensor hess_u;
    2602             : 
    2603           0 :   for (unsigned int l=0; l<num_dofs; l++)
    2604             :     {
    2605           0 :       hess_u.add_scaled (d2phi[l][0], (*sol)(dof_indices[l]));
    2606             :     }
    2607             : 
    2608           0 :   return hess_u;
    2609           0 : }
    2610             : 
    2611             : 
    2612             : 
    2613           0 : Tensor System::point_hessian(unsigned int var, const Point & p, const Elem * e) const
    2614             : {
    2615           0 :   libmesh_assert(e);
    2616           0 :   return this->point_hessian(var, p, *e);
    2617             : }
    2618             : 
    2619             : 
    2620             : 
    2621           0 : Tensor System::point_hessian(unsigned int var, const Point & p, const NumericVector<Number> * sol) const
    2622             : {
    2623           0 :   return this->point_hessian(var, p, true, sol);
    2624             : }
    2625             : 
    2626             : #else
    2627             : 
    2628             : Tensor System::point_hessian(unsigned int, const Point &, const bool,
    2629             :                              const NumericVector<Number> *) const
    2630             : {
    2631             :   libmesh_error_msg("We can only accumulate a hessian with --enable-second");
    2632             : 
    2633             :   // Avoid compiler warnings
    2634             :   return Tensor();
    2635             : }
    2636             : 
    2637             : Tensor System::point_hessian(unsigned int, const Point &, const Elem &,
    2638             :                              const NumericVector<Number> *) const
    2639             : {
    2640             :   libmesh_error_msg("We can only accumulate a hessian with --enable-second");
    2641             : 
    2642             :   // Avoid compiler warnings
    2643             :   return Tensor();
    2644             : }
    2645             : 
    2646             : Tensor System::point_hessian(unsigned int, const Point &, const Elem *) const
    2647             : {
    2648             :   libmesh_error_msg("We can only accumulate a hessian with --enable-second");
    2649             : 
    2650             :   // Avoid compiler warnings
    2651             :   return Tensor();
    2652             : }
    2653             : 
    2654             : Tensor System::point_hessian(unsigned int, const Point &, const NumericVector<Number> *) const
    2655             : {
    2656             :   libmesh_error_msg("We can only accumulate a hessian with --enable-second");
    2657             : 
    2658             :   // Avoid compiler warnings
    2659             :   return Tensor();
    2660             : }
    2661             : 
    2662             : #endif // LIBMESH_ENABLE_SECOND_DERIVATIVES
    2663             : 
    2664         560 : void System::create_static_condensation()
    2665             : {
    2666         560 :   this->get_dof_map().create_static_condensation(this->get_mesh(), *this);
    2667         560 : }
    2668             : 
    2669       87371 : bool System::has_static_condensation() const
    2670             : {
    2671       87371 :   return this->get_dof_map().has_static_condensation();
    2672             : }
    2673             : 
    2674   125798812 : unsigned int System::n_vars() const
    2675             : {
    2676   125798812 :   return this->get_dof_map().n_vars();
    2677             : }
    2678             : 
    2679    30610369 : const std::string & System::variable_name (const unsigned int i) const
    2680             : {
    2681    30610369 :   return this->get_dof_map().variable_name(i);
    2682             : }
    2683             : 
    2684           0 : bool System::identify_variable_groups () const
    2685             : {
    2686           0 :   return this->get_dof_map().identify_variable_groups();
    2687             : }
    2688             : 
    2689           0 : void System::identify_variable_groups (const bool ivg)
    2690             : {
    2691           0 :   this->get_dof_map().identify_variable_groups(ivg);
    2692           0 : }
    2693             : 
    2694           0 : unsigned int System::n_components() const
    2695             : {
    2696           0 :   return this->get_dof_map().n_components(this->get_mesh());
    2697             : }
    2698             : 
    2699      273913 : unsigned int System::n_variable_groups() const
    2700             : {
    2701      273913 :   return this->get_dof_map().n_variable_groups();
    2702             : }
    2703             : 
    2704    51609426 : const Variable & System::variable (const unsigned int i) const
    2705             : {
    2706    51609426 :   return this->get_dof_map().variable(i);
    2707             : }
    2708             : 
    2709      285704 : const VariableGroup & System::variable_group (const unsigned int vg) const
    2710             : {
    2711      285704 :   return this->get_dof_map().variable_group(vg);
    2712             : }
    2713             : 
    2714             : unsigned int
    2715     6651847 : System::variable_scalar_number (unsigned int var_num,
    2716             :                                 unsigned int component) const
    2717             : {
    2718     6651847 :   return this->get_dof_map().variable_scalar_number(var_num, component);
    2719             : }
    2720             : 
    2721    61847204 : const FEType & System::variable_type (const unsigned int i) const
    2722             : {
    2723    61847204 :   return this->get_dof_map().variable_type(i);
    2724             : }
    2725             : 
    2726       34524 : const FEType & System::variable_type (std::string_view var) const
    2727             : {
    2728       34524 :   return this->get_dof_map().variable_type(var);
    2729             : }
    2730             : 
    2731             : } // namespace libMesh

Generated by: LCOV version 1.14