LCOV - code coverage report
Current view: top level - src/systems - system.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4256 (26f7e2) with base d735cc Lines: 716 1117 64.1 %
Date: 2025-10-01 13:47:18 Functions: 105 160 65.6 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14