LCOV - code coverage report
Current view: top level - src/systems - system.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4624 (b6265e) with base 1fb77f Lines: 735 1141 64.4 %
Date: 2026-11-03 16:49:57 Functions: 107 166 64.5 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14