libMesh
equation_systems.C
Go to the documentation of this file.
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 // Local Includes
20 #include "libmesh/default_coupling.h" // For downconversion
21 #include "libmesh/dof_map.h"
22 #include "libmesh/eigen_system.h"
23 #include "libmesh/elem.h"
24 #include "libmesh/explicit_system.h"
25 #include "libmesh/fe_interface.h"
26 #include "libmesh/frequency_system.h"
27 #include "libmesh/int_range.h"
28 #include "libmesh/libmesh_logging.h"
29 #include "libmesh/linear_implicit_system.h"
30 #include "libmesh/mesh_base.h"
31 #include "libmesh/mesh_refinement.h"
32 #include "libmesh/newmark_system.h"
33 #include "libmesh/nonlinear_implicit_system.h"
34 #include "libmesh/parallel.h"
35 #include "libmesh/rb_construction.h"
36 #include "libmesh/remote_elem.h"
37 #include "libmesh/transient_rb_construction.h"
38 #include "libmesh/transient_system.h"
39 
40 // System includes
41 #include <functional> // std::plus
42 #include <numeric> // std::iota
43 #include <sstream>
44 
45 // Include the systems before this one to avoid
46 // overlapping forward declarations.
47 #include "libmesh/equation_systems.h"
48 
49 namespace libMesh
50 {
51 
53  ParallelObject (m),
54  _mesh (m),
55  _refine_in_reinit(true),
56  _enable_default_ghosting(true)
57 {
58  // Set default parameters
59  this->parameters.set<Real> ("linear solver tolerance") = TOLERANCE * TOLERANCE;
60  this->parameters.set<unsigned int>("linear solver maximum iterations") = 5000;
61 }
62 
63 
64 
66 
67 
68 
70 {
71  // Clear any additional parameters
72  parameters.clear ();
73 
74  // Clear the systems.
75  _systems.clear();
76 }
77 
78 
79 
81 {
82 #ifndef NDEBUG
83  for (auto i : make_range(this->n_systems()))
84  libmesh_assert(!this->get_system(i).is_initialized());
85 #endif
86 
87  this->reinit_mesh();
88 }
89 
90 
91 
93 {
94  const bool mesh_changed = this->reinit_solutions();
95 
96  // If the mesh has changed, systems will need to reinitialize their
97  // own data on the new mesh.
98  if (mesh_changed)
99  this->reinit_systems();
100 }
101 
103 {
104  const unsigned int n_sys = this->n_systems();
105 
106  libmesh_assert_not_equal_to (n_sys, 0);
107 
108  // Tell all the \p DofObject entities how many systems
109  // there are.
110  for (auto & node : _mesh.node_ptr_range())
111  node->set_n_systems(n_sys);
112 
115  [n_sys](const ElemRange & range)
116  {
117  for (Elem * elem : range)
118  elem->set_n_systems(n_sys);
119  });
120 
121  //for (auto i : make_range(this->n_systems()))
122  //this->get_system(i).init();
123 
124 #ifdef LIBMESH_ENABLE_AMR
125  MeshRefinement mesh_refine(_mesh);
126  mesh_refine.clean_refinement_flags();
127 #endif
128 
129  // Now loop over all the systems belonging to this ES
130  // and call reinit_mesh for each system
131  for (auto i : make_range(this->n_systems()))
132  this->get_system(i).reinit_mesh();
133 
134 }
135 
137 {
138  parallel_object_only();
139 
140  const unsigned int n_sys = this->n_systems();
141  libmesh_assert_not_equal_to (n_sys, 0);
142 
143  // And any new systems will need initialization
144  for (unsigned int i=0; i != n_sys; ++i)
145  if (!this->get_system(i).is_initialized())
146  this->get_system(i).init();
147 
148  // We used to assert that all nodes and elements *already* had
149  // n_systems() properly set; however this is false in the case where
150  // user code has manually added nodes and/or elements to an
151  // already-initialized system.
152 
153  // Make sure all the \p DofObject entities know how many systems
154  // there are.
155  {
156  // All the nodes
157  for (auto & node : _mesh.node_ptr_range())
158  node->set_n_systems(n_sys);
159 
160  // All the elements
163  [n_sys](const ElemRange & range)
164  {
165  for (Elem * elem : range)
166  elem->set_n_systems(n_sys);
167  });
168  }
169 
170  // Localize each system's vectors
171  for (unsigned int i=0; i != n_sys; ++i)
172  this->get_system(i).re_update();
173 
174 #ifdef LIBMESH_ENABLE_AMR
175 
176  bool mesh_changed = false;
177 
178  // FIXME: For backwards compatibility, assume
179  // refine_and_coarsen_elements or refine_uniformly have already
180  // been called
181  {
182  for (unsigned int i=0; i != n_sys; ++i)
183  {
184  System & sys = this->get_system(i);
185 
186  // Even if the system doesn't have any variables in it we want
187  // consistent behavior; e.g. distribute_dofs should have the
188  // opportunity to count up zero dofs on each processor.
189  //
190  // Who's been adding zero-var systems anyway, outside of my
191  // unit tests? - RHS
192  // if (!sys.n_vars())
193  // continue;
194 
196 
197  // Recreate any user or internal constraints
198  sys.reinit_constraints();
199 
200  // Even if there weren't any constraint changes,
201  // reinit_constraints() did prepare_send_list() for us.
202 
203  sys.prolong_vectors();
204  }
205  mesh_changed = true;
206  }
207 
208  if (this->_refine_in_reinit)
209  {
210  // Don't override any user refinement settings
211  MeshRefinement mesh_refine(_mesh);
212  mesh_refine.face_level_mismatch_limit() = 0; // unlimited
213  mesh_refine.overrefined_boundary_limit() = -1; // unlimited
214  mesh_refine.underrefined_boundary_limit() = -1; // unlimited
215 
216  // Try to coarsen the mesh, then restrict each system's vectors
217  // if necessary
218  if (mesh_refine.coarsen_elements())
219  {
220  for (auto i : make_range(this->n_systems()))
221  {
222  System & sys = this->get_system(i);
224  sys.reinit_constraints();
225 
226  // Even if there weren't any constraint changes,
227  // reinit_constraints() did prepare_send_list() for us.
228 
229  sys.restrict_vectors();
230  }
231  mesh_changed = true;
232  }
233 
234  // Once vectors are all restricted, we can delete
235  // children of coarsened elements
236  if (mesh_changed)
237  this->get_mesh().contract();
238 
239  // Try to refine the mesh, then prolong each system's vectors
240  // if necessary
241  if (mesh_refine.refine_elements())
242  {
243  for (auto i : make_range(this->n_systems()))
244  {
245  System & sys = this->get_system(i);
247  sys.reinit_constraints();
248 
249  // Even if there weren't any constraint changes,
250  // reinit_constraints() did prepare_send_list() for us.
251 
252  sys.prolong_vectors();
253  }
254  mesh_changed = true;
255  }
256  }
257 
258  return mesh_changed;
259 
260 #endif // #ifdef LIBMESH_ENABLE_AMR
261 
262  return false;
263 }
264 
265 
266 
268 {
269  for (auto i : make_range(this->n_systems()))
270  this->get_system(i).reinit();
271 }
272 
273 
274 
276 {
277  // A serial mesh means nothing needs to be done
278  if (_mesh.is_serial())
279  return;
280 
281  const unsigned int n_sys = this->n_systems();
282 
283  libmesh_assert_not_equal_to (n_sys, 0);
284 
285  // Gather the mesh
286  _mesh.allgather();
287 
288  // Tell all the \p DofObject entities how many systems
289  // there are.
290  for (auto & node : _mesh.node_ptr_range())
291  node->set_n_systems(n_sys);
292 
295  [n_sys](const ElemRange & range)
296  {
297  for (Elem * elem : range)
298  elem->set_n_systems(n_sys);
299  });
300 
301  // And distribute each system's dofs
302  for (auto i : make_range(this->n_systems()))
303  {
304  System & sys = this->get_system(i);
305  DofMap & dof_map = sys.get_dof_map();
306  dof_map.distribute_dofs(_mesh);
307 
308  // The user probably won't need constraint equations or the
309  // send_list after an allgather, but let's keep it in consistent
310  // shape just in case.
311  sys.reinit_constraints();
312 
313  // Even if there weren't any constraint changes,
314  // reinit_constraints() did prepare_send_list() for us.
315  }
316 }
317 
318 
319 
321 {
322  _enable_default_ghosting = enable;
323  MeshBase &mesh = this->get_mesh();
324 
325  if (enable)
326  mesh.add_ghosting_functor(mesh.default_ghosting());
327  else
328  mesh.remove_ghosting_functor(mesh.default_ghosting());
329 
330  for (auto i : make_range(this->n_systems()))
331  {
332  DofMap & dof_map = this->get_system(i).get_dof_map();
333  if (enable)
334  dof_map.add_default_ghosting();
335  else
336  dof_map.remove_default_ghosting();
337  }
338 }
339 
340 
341 
343 {
344  LOG_SCOPE("update()", "EquationSystems");
345 
346  // Localize each system's vectors
347  for (auto i : make_range(this->n_systems()))
348  this->get_system(i).update();
349 }
350 
351 
352 
353 System & EquationSystems::add_system (std::string_view sys_type,
354  std::string_view name)
355 {
356  // If the user already built a system with this name, we'll
357  // trust them and we'll use it. That way they can pre-add
358  // non-standard derived system classes, and if their restart file
359  // has some non-standard sys_type we won't throw an error.
360  if (_systems.count(name))
361  {
362  return this->get_system(name);
363  }
364  // Build a basic System
365  else if (sys_type == "Basic")
366  this->add_system<System> (name);
367 
368  // Build a Newmark system
369  else if (sys_type == "Newmark")
370  this->add_system<NewmarkSystem> (name);
371 
372  // Build an Explicit system
373  else if ((sys_type == "Explicit"))
374  this->add_system<ExplicitSystem> (name);
375 
376  // Build an Implicit system
377  else if ((sys_type == "Implicit") ||
378  (sys_type == "Steady" ))
379  this->add_system<ImplicitSystem> (name);
380 
381  // build a transient implicit linear system
382  else if ((sys_type == "Transient") ||
383  (sys_type == "TransientImplicit") ||
384  (sys_type == "TransientLinearImplicit"))
385  this->add_system<TransientLinearImplicitSystem> (name);
386 
387  // build a transient implicit nonlinear system
388  else if (sys_type == "TransientNonlinearImplicit")
389  this->add_system<TransientNonlinearImplicitSystem> (name);
390 
391  // build a transient explicit system
392  else if (sys_type == "TransientExplicit")
393  this->add_system<TransientExplicitSystem> (name);
394 
395  // build a linear implicit system
396  else if (sys_type == "LinearImplicit")
397  this->add_system<LinearImplicitSystem> (name);
398 
399  // build a nonlinear implicit system
400  else if (sys_type == "NonlinearImplicit")
401  this->add_system<NonlinearImplicitSystem> (name);
402 
403  // build a Reduced Basis Construction system
404  else if (sys_type == "RBConstruction")
405  this->add_system<RBConstruction> (name);
406 
407  // build a transient Reduced Basis Construction system
408  else if (sys_type == "TransientRBConstruction")
409  this->add_system<TransientRBConstruction> (name);
410 
411 #ifdef LIBMESH_HAVE_SLEPC
412  // build an eigen system
413  else if (sys_type == "Eigen")
414  this->add_system<EigenSystem> (name);
415  else if (sys_type == "TransientEigenSystem")
416  this->add_system<TransientEigenSystem> (name);
417 #endif
418 
419 #if defined(LIBMESH_USE_COMPLEX_NUMBERS)
420  // build a frequency system
421  else if (sys_type == "Frequency")
422  this->add_system<FrequencySystem> (name);
423 #endif
424 
425  else
426  libmesh_error_msg("ERROR: Unknown system type: " << sys_type);
427 
428  // Return a reference to the new system
429  //return (*this)(name);
430  return this->get_system(name);
431 }
432 
433 
434 
436 {
437  libmesh_assert (this->n_systems());
438 
439  for (auto i : make_range(this->n_systems()))
440  this->get_system(i).solve();
441 }
442 
443 
444 
446 {
447  libmesh_assert (this->n_systems());
448 
449  for (auto i : make_range(this->n_systems()))
450  this->get_system(i).sensitivity_solve(parameters_in);
451 }
452 
453 
454 
455 void EquationSystems::adjoint_solve (const QoISet & qoi_indices)
456 {
457  libmesh_assert (this->n_systems());
458 
459  for (unsigned int i=this->n_systems(); i != 0; --i)
460  this->get_system(i-1).adjoint_solve(qoi_indices);
461 }
462 
463 
464 
465 void EquationSystems::build_variable_names (std::vector<std::string> & var_names,
466  const FEType * type,
467  const std::set<std::string> * system_names) const
468 {
469  // start indexing at end of possibly non-empty vector of variable names to avoid overwriting them
470  unsigned int var_num = var_names.size();
471 
472  // We'll want to double-check that we don't have any naming
473  // conflicts; this API causes problems down the line if so.
474  std::unordered_multiset<std::string> seen_names;
475 
476  // Need to size var_names by scalar variables plus all the
477  // vector components for all the vector variables
478  //Could this be replaced by a/some convenience methods?[PB]
479  {
480  unsigned int n_scalar_vars = 0;
481  unsigned int n_vector_vars = 0;
482 
483  for (const auto & [sys_name, sys_ptr] : _systems)
484  {
485  // Check current system is listed in system_names, and skip pos if not
486  bool use_current_system = (system_names == nullptr);
487  if (!use_current_system)
488  use_current_system = system_names->count(sys_name);
489  if (!use_current_system || sys_ptr->hide_output())
490  {
491  for (auto vn : make_range(sys_ptr->n_vars()))
492  seen_names.insert(sys_ptr->variable_name(vn));
493  continue;
494  }
495 
496  for (auto vn : make_range(sys_ptr->n_vars()))
497  {
498  seen_names.insert(sys_ptr->variable_name(vn));
499  if (FEInterface::field_type(sys_ptr->variable_type(vn)) == TYPE_VECTOR)
500  n_vector_vars++;
501  else
502  n_scalar_vars++;
503  }
504  }
505 
506  // Here, we're assuming the number of vector components is the same
507  // as the mesh spatial dimension.
508  unsigned int dim = this->get_mesh().spatial_dimension();
509  unsigned int nv = n_scalar_vars + dim*n_vector_vars;
510 
511  // We'd better not have more than dim*this->n_vars() (all vector variables)
512  // Treat the NodeElem-only mesh case as dim=1
513  libmesh_assert_less_equal ( nv, (dim > 0 ? dim : 1)*this->n_vars() );
514 
515  // 'nv' represents the max possible number of output variables, so allocate enough memory for
516  // all variables in the system to be populated here. When this is called more than once on a
517  // single 'var_names' vector, different filters should be used such that duplicates don't occur.
518  var_names.resize( nv );
519  }
520 
521  for (const auto & [sys_name, sys_ptr] : _systems)
522  {
523  // Check current system is listed in system_names, and skip pos if not
524  bool use_current_system = (system_names == nullptr);
525  if (!use_current_system)
526  use_current_system = system_names->count(sys_name);
527  if (!use_current_system || sys_ptr->hide_output())
528  continue;
529 
530  for (auto vn : make_range(sys_ptr->n_vars()))
531  {
532  const std::string & var_name = sys_ptr->variable_name(vn);
533  const FEType & fe_type = sys_ptr->variable_type(vn);
534 
535  unsigned int n_vec_dim = FEInterface::n_vec_dim( sys_ptr->get_mesh(), fe_type);
536 
537  // Filter on the type if requested
538  if (type == nullptr || (type && *type == fe_type))
539  {
540  if (FEInterface::field_type(fe_type) == TYPE_VECTOR)
541  {
542  switch(n_vec_dim)
543  {
544  case 0:
545  case 1:
546  var_names[var_num++] = var_name;
547  libmesh_error_msg_if(seen_names.count(var_name) > 1,
548  "Duplicate variable name "+var_name);
549  break;
550  case 2:
551  var_names[var_num++] = var_name+"_x";
552  var_names[var_num++] = var_name+"_y";
553  libmesh_error_msg_if(seen_names.count(var_name+"_x"),
554  "Duplicate variable name "+var_name+"_x");
555  libmesh_error_msg_if(seen_names.count(var_name+"_y"),
556  "Duplicate variable name "+var_name+"_y");
557  break;
558  case 3:
559  var_names[var_num++] = var_name+"_x";
560  var_names[var_num++] = var_name+"_y";
561  var_names[var_num++] = var_name+"_z";
562  libmesh_error_msg_if(seen_names.count(var_name+"_x"),
563  "Duplicate variable name "+var_name+"_x");
564  libmesh_error_msg_if(seen_names.count(var_name+"_y"),
565  "Duplicate variable name "+var_name+"_y");
566  libmesh_error_msg_if(seen_names.count(var_name+"_z"),
567  "Duplicate variable name "+var_name+"_z");
568  break;
569  default:
570  libmesh_error_msg("Invalid dim in build_variable_names");
571  }
572  }
573  else
574  var_names[var_num++] = var_name;
575  }
576  }
577  }
578  // Now resize again in case we filtered any names
579  var_names.resize(var_num);
580 }
581 
582 
583 
584 void EquationSystems::build_solution_vector (std::vector<Number> &,
585  std::string_view,
586  std::string_view) const
587 {
588  // TODO:[BSK] re-implement this from the method below
589  libmesh_not_implemented();
590 }
591 
592 
593 
594 
595 std::unique_ptr<NumericVector<Number>>
596 EquationSystems::build_parallel_solution_vector(const std::set<std::string> * system_names,
597  bool add_sides) const
598 {
599  LOG_SCOPE("build_parallel_solution_vector()", "EquationSystems");
600 
601  // This function must be run on all processors at once
602  parallel_object_only();
603 
604  const unsigned int dim = _mesh.spatial_dimension();
605  const dof_id_type max_nn = _mesh.max_node_id();
606 
607  // allocate vector storage to hold
608  // (max_node_id)*(number_of_variables) entries.
609  //
610  // If node renumbering is disabled and adaptive coarsening has
611  // created gaps between node numbers, then this vector will be
612  // sparse.
613  //
614  // We have to differentiate between between scalar and vector
615  // variables. We intercept vector variables and treat each
616  // component as a scalar variable (consistently with build_solution_names).
617 
618  unsigned int nv = 0;
619 
620  //Could this be replaced by a/some convenience methods?[PB]
621  {
622  unsigned int n_scalar_vars = 0;
623  unsigned int n_vector_vars = 0;
624  for (const auto & [sys_name, sys_ptr] : _systems)
625  {
626  // Check current system is listed in system_names, and skip pos if not
627  bool use_current_system = (system_names == nullptr);
628  if (!use_current_system)
629  use_current_system = system_names->count(sys_name);
630  if (!use_current_system || sys_ptr->hide_output())
631  continue;
632 
633  for (auto vn : make_range(sys_ptr->n_vars()))
634  {
635  if (FEInterface::field_type(sys_ptr->variable_type(vn)) == TYPE_VECTOR)
636  n_vector_vars++;
637  else
638  n_scalar_vars++;
639  }
640  }
641  // Here, we're assuming the number of vector components is the same
642  // as the mesh spatial dimension.
643  nv = n_scalar_vars + dim*n_vector_vars;
644  }
645 
646  // Get the number of nodes to store locally.
647  dof_id_type n_local_nodes = cast_int<dof_id_type>
648  (std::distance(_mesh.local_nodes_begin(),
649  _mesh.local_nodes_end()));
650 
651  // If node renumbering has been disabled, nodes may not be numbered
652  // contiguously, and the number of nodes might not match the
653  // max_node_id. In this case we just do our best.
654  dof_id_type n_total_nodes = n_local_nodes;
655  _mesh.comm().sum(n_total_nodes);
656 
657  const processor_id_type n_proc = _mesh.comm().size();
658  const processor_id_type my_pid = _mesh.comm().rank();
659  const dof_id_type n_gaps = max_nn - n_total_nodes;
660  const dof_id_type gaps_per_processor = n_gaps / n_proc;
661  const dof_id_type remainder_gaps = n_gaps % n_proc;
662 
663  n_local_nodes = n_local_nodes + // Actual nodes
664  gaps_per_processor + // Our even share of gaps
665  (my_pid < remainder_gaps); // Leftovers
666 
667  // If we've been asked to build added sides' data, we need space to
668  // add it. Keep track of how much space.
669  dof_id_type local_added_side_nodes = 0,
670  added_side_nodes = 0;
671 
672  // others_added_side_nodes[p]: local_added_side_nodes on rank p
673  std::vector<dof_id_type> others_added_side_nodes;
674 
675  // A map of (element_id, side, side_node) pairs to the corresponding
676  // added side node index.
677  std::map<std::tuple<dof_id_type, unsigned short, unsigned short>,
678  dof_id_type> discontinuous_node_indices;
679 
680  // If we don't have any added side nodes, we'll have no offsets from
681  // them, and we won't care about which offsets apply to which node
682  // ids either.
683 
684  // Number of true nodes on processors [0,p]
685  std::vector<dof_id_type> true_node_offsets;
686  // Number of added (fake) nodes on processors [0,p)
687  std::vector<dof_id_type> added_node_offsets;
688 
689  auto node_id_to_vec_id =
690  [&true_node_offsets, &added_node_offsets]
691  (const dof_id_type node_id)
692  {
693  if (true_node_offsets.empty())
694  return node_id; // O(1) in the common !add_sides case
695 
696  // Find the processor id that has node_id in the parallel vec
697  const auto lb = std::upper_bound(true_node_offsets.begin(),
698  true_node_offsets.end(), node_id);
699  libmesh_assert(lb != true_node_offsets.end());
700  const processor_id_type p = lb - true_node_offsets.begin();
701 
702  return node_id + added_node_offsets[p];
703  };
704 
705  if (add_sides)
706  {
707  true_node_offsets.resize(n_proc);
708  added_node_offsets.resize(n_proc);
709 
710  // One loop to count everyone's new side nodes
711  for (const auto & elem : _mesh.active_element_ptr_range())
712  {
713  for (auto s : elem->side_index_range())
714  {
715  if (redundant_added_side(*elem,s))
716  continue;
717 
718  const std::vector<unsigned int> side_nodes =
719  elem->nodes_on_side(s);
720 
721  if (elem->processor_id() == this->processor_id())
722  local_added_side_nodes += side_nodes.size();
723  }
724  }
725 
726  others_added_side_nodes.resize(n_proc);
727  _mesh.comm().allgather(local_added_side_nodes,
728  others_added_side_nodes);
729 
730  added_side_nodes = std::accumulate(others_added_side_nodes.begin(),
731  others_added_side_nodes.end(), 0,
732  std::plus<>());
733 
734  _mesh.comm().allgather(n_local_nodes, true_node_offsets);
735  for (auto p : make_range(n_proc-1))
736  true_node_offsets[p+1] += true_node_offsets[p];
737  libmesh_assert_equal_to(true_node_offsets[n_proc-1], _mesh.max_node_id());
738 
739  // For nodes that exist in the mesh, we just need an offset to
740  // tell where to put their solutions.
741  added_node_offsets[0] = 0;
742  for (auto p : make_range(n_proc-1))
743  added_node_offsets[p+1] =
744  added_node_offsets[p] + others_added_side_nodes[p];
745 
746  // For added side nodes, we need to fill a map. Start after all
747  // the true node for our pid plus all the side nodes for
748  // previous pids
749  dof_id_type node_counter = true_node_offsets[my_pid];
750  for (auto p : make_range(my_pid))
751  node_counter += others_added_side_nodes[p];
752 
753  // One loop to figure out whose added side nodes get which index
754  for (const auto & elem : _mesh.active_local_element_ptr_range())
755  {
756  for (auto s : elem->side_index_range())
757  {
758  if (redundant_added_side(*elem,s))
759  continue;
760 
761  const std::vector<unsigned int> side_nodes =
762  elem->nodes_on_side(s);
763 
764  for (auto n : index_range(side_nodes))
765  discontinuous_node_indices
766  [std::make_tuple(elem->id(),s,n)] = node_counter++;
767  }
768  }
769  }
770 
771  const dof_id_type
772  n_global_vals = (max_nn + added_side_nodes) * nv,
773  n_local_vals = (n_local_nodes + local_added_side_nodes) * nv;
774 
775  // Create a NumericVector to hold the parallel solution
776  std::unique_ptr<NumericVector<Number>> parallel_soln_ptr = NumericVector<Number>::build(_communicator);
777  NumericVector<Number> & parallel_soln = *parallel_soln_ptr;
778  parallel_soln.init(n_global_vals, n_local_vals, false, PARALLEL);
779 
780  // Create a NumericVector to hold the "repeat_count" for each node - this is essentially
781  // the number of elements contributing to that node's value
782  std::unique_ptr<NumericVector<Number>> repeat_count_ptr = NumericVector<Number>::build(_communicator);
783  NumericVector<Number> & repeat_count = *repeat_count_ptr;
784  repeat_count.init(n_global_vals, n_local_vals, false, PARALLEL);
785 
786  repeat_count.close();
787 
788  unsigned int var_num=0;
789 
790  // For each system in this EquationSystems object,
791  // update the global solution and if we are on processor 0,
792  // loop over the elements and build the nodal solution
793  // from the element solution. Then insert this nodal solution
794  // into the vector passed to build_solution_vector.
795  for (const auto & [sys_name, sys_ptr] : _systems)
796  {
797  // Check current system is listed in system_names, and skip pos if not
798  bool use_current_system = (system_names == nullptr);
799  if (!use_current_system)
800  use_current_system = system_names->count(sys_name);
801  if (!use_current_system || sys_ptr->hide_output())
802  continue;
803 
804  const System & system = *sys_ptr;
805  const unsigned int nv_sys = system.n_vars();
806  const unsigned int sys_num = system.number();
807 
808  //Could this be replaced by a/some convenience methods?[PB]
809  unsigned int n_scalar_vars = 0;
810  unsigned int n_vector_vars = 0;
811  for (auto vn : make_range(sys_ptr->n_vars()))
812  {
813  if (FEInterface::field_type(sys_ptr->variable_type(vn)) == TYPE_VECTOR)
814  n_vector_vars++;
815  else
816  n_scalar_vars++;
817  }
818 
819  // Here, we're assuming the number of vector components is the same
820  // as the mesh spatial dimension.
821  unsigned int nv_sys_split = n_scalar_vars + dim*n_vector_vars;
822 
823  // Update the current_local_solution
824  {
825  System & non_const_sys = const_cast<System &>(system);
826  // We used to simply call non_const_sys.solution->close()
827  // here, but that is not allowed when the solution vector is
828  // locked read-only, for example when printing the solution
829  // during the middle of a solve... So try to be a bit
830  // more careful about calling close() unnecessarily.
831  libmesh_assert(this->comm().verify(non_const_sys.solution->closed()));
832  if (!non_const_sys.solution->closed())
833  non_const_sys.solution->close();
834  non_const_sys.update();
835  }
836 
837  NumericVector<Number> & sys_soln(*system.current_local_solution);
838 
839  const DofMap & dof_map = system.get_dof_map();
840 
841  std::vector<Number> elem_soln; // The finite element solution
842  std::vector<Number> nodal_soln; // The FE solution interpolated to the nodes
843  std::vector<dof_id_type> dof_indices; // The DOF indices for the finite element
844 
845  unsigned var_inc = 0;
846  for (unsigned int var=0; var<nv_sys; var++)
847  {
848  const FEType & fe_type = system.variable_type(var);
849  const Variable & var_description = system.variable(var);
850  unsigned int n_vec_dim = FEInterface::n_vec_dim( sys_ptr->get_mesh(), fe_type );
851  const bool add_p_level = fe_type.p_refinement;
852 
853  for (const auto & elem : _mesh.active_local_element_ptr_range())
854  {
855  if (var_description.active_on_subdomain(elem->subdomain_id()))
856  {
857  dof_map.dof_indices (elem, dof_indices, var);
858  sys_soln.get(dof_indices, elem_soln);
859 
860  FEInterface::nodal_soln (elem->dim(),
861  fe_type,
862  elem,
863  elem_soln,
864  nodal_soln,
865  add_p_level,
866  n_vec_dim);
867 
868  // infinite elements should be skipped...
869  if (!elem->infinite())
870  {
871  libmesh_assert_equal_to (nodal_soln.size(), n_vec_dim*elem->n_nodes());
872 
873  for (auto n : elem->node_index_range())
874  {
875  const Node & node = elem->node_ref(n);
876 
877  const dof_id_type node_idx =
878  nv * node_id_to_vec_id(node.id());
879 
880  for (unsigned int d=0; d < n_vec_dim; d++)
881  {
882  // For vector-valued elements, all components are in nodal_soln. For each
883  // node, the components are stored in order, i.e. node_0 -> s0_x, s0_y, s0_z
884  parallel_soln.add(node_idx + (var_inc+d + var_num), nodal_soln[n_vec_dim*n+d]);
885 
886  // Increment the repeat count for this position
887  repeat_count.add(node_idx + (var_inc+d + var_num), 1);
888  }
889  }
890 
891  if (add_sides)
892  {
893  for (auto s : elem->side_index_range())
894  {
895  if (redundant_added_side(*elem,s))
896  continue;
897 
898  // Compute the FE solution at all the
899  // side nodes
901  (fe_type, elem, s, elem_soln,
902  nodal_soln, add_p_level, n_vec_dim);
903 
904 #ifdef DEBUG
905  const std::vector<unsigned int> side_nodes =
906  elem->nodes_on_side(s);
907 
908  libmesh_assert_equal_to
909  (nodal_soln.size(),
910  side_nodes.size());
911 #endif
912 
913  for (auto n : index_range(nodal_soln))
914  {
915  // Retrieve index into global solution vector.
916  std::size_t node_index =
917  nv * libmesh_map_find(discontinuous_node_indices,
918  std::make_tuple(elem->id(), s, n));
919 
920  for (unsigned int d=0; d < n_vec_dim; d++)
921  {
922  parallel_soln.add(node_index + (var_inc+d + var_num), nodal_soln[n_vec_dim*n+d]);
923  repeat_count.add(node_index + (var_inc+d + var_num), 1);
924  }
925  }
926  }
927  }
928  }
929  }
930  else // If this variable doesn't exist on this subdomain we have to still increment repeat_count so that we won't divide by 0 later:
931  for (auto n : elem->node_index_range())
932  {
933  const Node & node = elem->node_ref(n);
934  // Only do this if this variable has NO DoFs at
935  // this node... it might have some from an
936  // adjoining element...
937  if (!node.n_dofs(sys_num, var))
938  {
939  const dof_id_type node_idx =
940  nv * node_id_to_vec_id(node.id());
941 
942  for (unsigned int d=0; d < n_vec_dim; d++)
943  repeat_count.add(node_idx + (var_inc+d + var_num), 1);
944  }
945  }
946 
947  } // end loop over elements
948  var_inc += n_vec_dim;
949  } // end loop on variables in this system
950 
951  var_num += nv_sys_split;
952  } // end loop over systems
953 
954  // Sum the nodal solution values and repeat counts.
955  parallel_soln.close();
956  repeat_count.close();
957 
958  // If there were gaps in the node numbering, there will be
959  // corresponding zeros in the parallel_soln and repeat_count
960  // vectors. We need to set those repeat_count entries to 1
961  // in order to avoid dividing by zero.
962  if (n_gaps)
963  {
964  for (numeric_index_type i=repeat_count.first_local_index();
965  i<repeat_count.last_local_index(); ++i)
966  {
967  // repeat_count entries are integral values but let's avoid a
968  // direct floating point comparison with 0 just in case some
969  // roundoff noise crept in during vector assembly?
970  if (std::abs(repeat_count(i)) < TOLERANCE)
971  repeat_count.set(i, 1.);
972  }
973 
974  // Make sure the repeat_count vector is up-to-date on all
975  // processors.
976  repeat_count.close();
977  }
978 
979  // Divide to get the average value at the nodes
980  parallel_soln /= repeat_count;
981 
982  return parallel_soln_ptr;
983 }
984 
985 
986 
987 void EquationSystems::build_solution_vector (std::vector<Number> & soln,
988  const std::set<std::string> * system_names,
989  bool add_sides) const
990 {
991  LOG_SCOPE("build_solution_vector()", "EquationSystems");
992 
993  // Call the parallel implementation
994  std::unique_ptr<NumericVector<Number>> parallel_soln =
995  this->build_parallel_solution_vector(system_names, add_sides);
996 
997  // Localize the NumericVector into the provided std::vector.
998  parallel_soln->localize_to_one(soln);
999 }
1000 
1001 
1002 
1003 void EquationSystems::get_vars_active_subdomains(const std::vector<std::string> & names,
1004  std::vector<std::set<subdomain_id_type>> & vars_active_subdomains) const
1005 {
1006  vars_active_subdomains.clear();
1007  vars_active_subdomains.resize(names.size());
1008 
1009  for (const auto & pr : _systems)
1010  {
1011  const auto & sys_ptr = pr.second;
1012  for (auto vn : make_range(sys_ptr->n_vars()))
1013  {
1014  const std::string & var_name = sys_ptr->variable_name(vn);
1015 
1016  auto names_it = std::find(names.begin(), names.end(), var_name);
1017  if(names_it != names.end())
1018  {
1019  const Variable & variable = sys_ptr->variable(vn);
1020  const std::set<subdomain_id_type> & active_subdomains = variable.active_subdomains();
1021  vars_active_subdomains[std::distance(names.begin(), names_it)] = active_subdomains;
1022  }
1023  }
1024  }
1025 }
1026 
1027 
1028 
1029 void
1031  std::vector<std::string> & names) const
1032 {
1033  // Call the parallel version of this function
1034  std::unique_ptr<NumericVector<Number>> parallel_soln =
1036 
1037  // Localize into 'soln', provided that parallel_soln is not empty.
1038  // Note: parallel_soln will be empty in the event that none of the
1039  // input names were CONSTANT, MONOMIAL nor components of CONSTANT,
1040  // MONOMIAL_VEC variables, or there were simply none of these in
1041  // the EquationSystems object.
1042  soln.clear();
1043  if (parallel_soln)
1044  parallel_soln->localize_to_one(soln);
1045 }
1046 
1047 std::vector<std::pair<unsigned int, unsigned int>>
1049  (std::vector<std::string> & names, const FEType * type, const std::vector<FEType> * types) const
1050 {
1051  // This function must be run on all processors at once
1052  parallel_object_only();
1053 
1054  libmesh_assert (this->n_systems());
1055 
1056  // Resolve class of type input and assert that at least one of them is null
1057  libmesh_assert_msg(!type || !types,
1058  "Input 'type', 'types', or neither in find_variable_numbers, but not both.");
1059 
1060  std::vector<FEType> type_filter;
1061  if (type)
1062  type_filter.push_back(*type);
1063  else if (types)
1064  type_filter = *types;
1065 
1066  // Store a copy of the valid variable names, if any. The names vector will be repopulated with any
1067  // valid names (or all if 'is_names_empty') in the system that passes through the type filter. If
1068  // the variable is a vector, its name will be decomposed into its separate components in
1069  // accordance with build_variable_names().
1070  std::vector<std::string> name_filter = names;
1071  bool is_names_empty = name_filter.empty();
1072  names.clear();
1073 
1074  // initialize convenience variables
1075  FEType var_type;
1076  std::string name;
1077 
1078  const std::vector<std::string> component_suffix = {"_x", "_y", "_z"};
1079  unsigned int dim = _mesh.spatial_dimension();
1080  libmesh_error_msg_if(dim > 3, "Invalid dim in find_variable_numbers");
1081 
1082  // Now filter through the variables in each system and store the system index and their index
1083  // within that system. This way, we know where to find their data even after we sort them.
1084  std::vector<std::pair<unsigned int, unsigned int>> var_nums;
1085 
1086  for (const auto & pr : _systems)
1087  {
1088  const System & system = *(pr.second);
1089 
1090  for (auto var : make_range(system.n_vars()))
1091  {
1092  // apply the type filter
1093  var_type = system.variable_type(var);
1094  if (type_filter.size() &&
1095  std::find(type_filter.begin(), type_filter.end(), var_type) == type_filter.end())
1096  continue;
1097 
1098  // apply the name filter (note that all variables pass if it is empty)
1099  if (FEInterface::field_type(var_type) == TYPE_VECTOR)
1100  {
1101  std::vector<std::string> component_names;
1102  for (unsigned int comp = 0; comp < dim; ++comp)
1103  {
1104  name = system.variable_name(var) + component_suffix[comp];
1105  if (is_names_empty ||
1106  (std::find(name_filter.begin(), name_filter.end(), name) != name_filter.end()))
1107  component_names.push_back(name);
1108  }
1109 
1110  if (! component_names.empty())
1111  names.insert(names.end(), component_names.begin(), component_names.end());
1112  else
1113  continue;
1114  }
1115  else /*scalar-valued variable*/
1116  {
1117  name = system.variable_name(var);
1118  if (is_names_empty ||
1119  (std::find(name_filter.begin(), name_filter.end(), name) != name_filter.end()))
1120  names.push_back(name);
1121  else
1122  continue;
1123  }
1124 
1125  // if the variable made it through both filters get its system indices
1126  var_nums.emplace_back(system.number(), var);
1127  }
1128  }
1129 
1130  // Sort the var_nums vector pairs alphabetically based on the variable name
1131  std::vector<unsigned int> sort_index(var_nums.size());
1132  std::iota(sort_index.begin(), sort_index.end(), 0);
1133  std::sort(sort_index.begin(), sort_index.end(),
1134  [&](const unsigned int & lhs, const unsigned int & rhs)
1135  {return this->get_system(var_nums[lhs].first).variable_name(var_nums[lhs].second) <
1136  this->get_system(var_nums[rhs].first).variable_name(var_nums[rhs].second);});
1137 
1138  std::vector<std::pair<unsigned int, unsigned int>> var_nums_sorted(var_nums.size());
1139  for (auto i : index_range(var_nums_sorted))
1140  {
1141  var_nums_sorted[i].first = var_nums[sort_index[i]].first;
1142  var_nums_sorted[i].second = var_nums[sort_index[i]].second;
1143  }
1144 
1145  // Also sort the names vector
1146  std::sort(names.begin(), names.end());
1147 
1148  // Return the sorted vector pairs
1149  return var_nums_sorted;
1150 }
1151 
1152 
1153 std::unique_ptr<NumericVector<Number>>
1154 EquationSystems::build_parallel_elemental_solution_vector (std::vector<std::string> & names) const
1155 {
1156  // Filter any names that aren't elemental variables and get the system indices for those that are.
1157  // Note that it's probably fine if the names vector is empty since we'll still at least filter
1158  // out all non-monomials. If there are no monomials, then nothing is output here.
1159  std::vector<FEType> type = {FEType(CONSTANT, MONOMIAL), FEType(CONSTANT, MONOMIAL_VEC)};
1160  std::vector<std::pair<unsigned int, unsigned int>> var_nums =
1161  this->find_variable_numbers(names, /*type=*/nullptr, &type);
1162 
1163  const std::size_t nv = names.size(); /*total number of vars including vector components*/
1164  const dof_id_type ne = _mesh.n_elem();
1165  libmesh_assert_equal_to (ne, _mesh.max_elem_id());
1166 
1167  // If there are no variables to write out don't do anything...
1168  if (!nv)
1169  return std::unique_ptr<NumericVector<Number>>(nullptr);
1170 
1171  // We can handle the case where there are nullptrs in the Elem vector
1172  // by just having extra zeros in the solution vector.
1173  numeric_index_type parallel_soln_global_size = ne*nv;
1174 
1175  numeric_index_type div = parallel_soln_global_size / this->n_processors();
1176  numeric_index_type mod = parallel_soln_global_size % this->n_processors();
1177 
1178  // Initialize all processors to the average size.
1179  numeric_index_type parallel_soln_local_size = div;
1180 
1181  // The first "mod" processors get an extra entry.
1182  if (this->processor_id() < mod)
1183  parallel_soln_local_size = div+1;
1184 
1185  // Create a NumericVector to hold the parallel solution
1186  std::unique_ptr<NumericVector<Number>> parallel_soln_ptr = NumericVector<Number>::build(_communicator);
1187  NumericVector<Number> & parallel_soln = *parallel_soln_ptr;
1188  parallel_soln.init(parallel_soln_global_size,
1189  parallel_soln_local_size,
1190  /*fast=*/false,
1191  /*ParallelType=*/PARALLEL);
1192 
1193  unsigned int sys_ctr = 0;
1194  unsigned int var_ctr = 0;
1195  for (auto i : index_range(var_nums))
1196  {
1197  std::pair<unsigned int, unsigned int> var_num = var_nums[i];
1198  const System & system = this->get_system(var_num.first);
1199 
1200  // Update the current_local_solution if necessary
1201  if (sys_ctr != var_num.first)
1202  {
1203  System & non_const_sys = const_cast<System &>(system);
1204  // We used to simply call non_const_sys.solution->close()
1205  // here, but that is not allowed when the solution vector is
1206  // locked read-only, for example when printing the solution
1207  // during during the middle of a solve... So try to be a bit
1208  // more careful about calling close() unnecessarily.
1209  libmesh_assert(this->comm().verify(non_const_sys.solution->closed()));
1210  if (!non_const_sys.solution->closed())
1211  non_const_sys.solution->close();
1212  non_const_sys.update();
1213  sys_ctr = var_num.first;
1214  }
1215 
1216  NumericVector<Number> & sys_soln(*system.current_local_solution);
1217 
1218  const unsigned int var = var_num.second;
1219 
1220  const Variable & variable = system.variable(var);
1221  const DofMap & dof_map = system.get_dof_map();
1222 
1223  // We need to check if the constant monomial is a scalar or a vector and set the number of
1224  // components as the mesh spatial dimension for the latter as per es.find_variable_numbers().
1225  // Even for the case where a variable is not active on any subdomain belonging to the
1226  // processor, we still need to know this number to update 'var_ctr'.
1227  const unsigned int n_comps =
1228  (system.variable_type(var) == type[1]) ? _mesh.spatial_dimension() : 1;
1229 
1230  // Loop over all elements in the mesh and index all components of the variable if it's active
1233  [&dof_map, &variable, ne, var, var_ctr, n_comps,
1234  &parallel_soln, &sys_soln](const ConstElemRange & range)
1235  {
1236  // The DOF indices for the finite element
1237  std::vector<dof_id_type> dof_indices;
1238 
1239  for (const Elem * elem : range)
1240  {
1241  if (variable.active_on_subdomain(elem->subdomain_id()))
1242  {
1243  dof_map.dof_indices(elem, dof_indices, var);
1244 
1245  // The number of DOF components needs to be equal to the expected number so that we know
1246  // where to store data to correctly correspond to variable names.
1247  libmesh_assert_equal_to(dof_indices.size(), n_comps);
1248 
1249  for (unsigned int comp = 0; comp < n_comps; comp++)
1250  parallel_soln.set(ne * (var_ctr + comp) + elem->id(), sys_soln(dof_indices[comp]));
1251  }
1252  }
1253  });
1254 
1255  var_ctr += n_comps;
1256  } // end loop over var_nums
1257 
1258  // NOTE: number of output names might not be equal to the number passed to this function. Any that
1259  // aren't CONSTANT MONOMIALS or components of CONSTANT MONOMIAL_VECS have been filtered out (see
1260  // EquationSystems::find_variable_numbers).
1261  //
1262  // But, if everything is accounted for properly, then names.size() == var_ctr
1263  libmesh_assert_equal_to(names.size(), var_ctr);
1264 
1265  parallel_soln.close();
1266  return parallel_soln_ptr;
1267 }
1268 
1269 
1270 
1271 void
1273 (std::vector<Number> & soln,
1274  const std::set<std::string> * system_names,
1275  const std::vector<std::string> * var_names,
1276  bool vertices_only,
1277  bool add_sides) const
1278 {
1279  LOG_SCOPE("build_discontinuous_solution_vector()", "EquationSystems");
1280 
1281  libmesh_assert (this->n_systems());
1282 
1283  // Get the number of variables (nv) by counting the number of variables
1284  // in each system listed in system_names
1285  unsigned int nv = 0;
1286 
1287  for (const auto & [sys_name, sys_ptr] : _systems)
1288  {
1289  // Check current system is listed in system_names, and skip pos if not
1290  bool use_current_system = (system_names == nullptr);
1291  if (!use_current_system)
1292  use_current_system = system_names->count(sys_name);
1293  if (!use_current_system || sys_ptr->hide_output())
1294  continue;
1295 
1296  // Loop over all variables in this System and check whether we
1297  // are supposed to use each one.
1298  for (auto var_id : make_range(sys_ptr->n_vars()))
1299  {
1300  bool use_current_var = (var_names == nullptr);
1301  if (!use_current_var)
1302  use_current_var = std::count(var_names->begin(),
1303  var_names->end(),
1304  sys_ptr->variable_name(var_id));
1305 
1306  // Only increment the total number of vars if we are
1307  // supposed to use this one.
1308  if (use_current_var)
1309  nv++;
1310  }
1311  }
1312 
1313  // get the total "weight" - the number of nodal values to write for
1314  // each variable.
1315  unsigned int tw=0;
1316  for (const auto & elem : _mesh.active_element_ptr_range())
1317  {
1318  tw += vertices_only ? elem->n_vertices() : elem->n_nodes();
1319 
1320  if (add_sides)
1321  {
1322  for (auto s : elem->side_index_range())
1323  {
1324  if (redundant_added_side(*elem,s))
1325  continue;
1326 
1327  const std::vector<unsigned int> side_nodes =
1328  elem->nodes_on_side(s);
1329 
1330  if (!vertices_only)
1331  tw += side_nodes.size();
1332  else
1333  for (auto n : index_range(side_nodes))
1334  if (elem->is_vertex(side_nodes[n]))
1335  ++tw;
1336  }
1337  }
1338  }
1339 
1340  // Only if we are on processor zero, allocate the storage
1341  // to hold (number_of_nodes)*(number_of_variables) entries.
1342  if (_mesh.processor_id() == 0)
1343  soln.resize(tw*nv);
1344 
1345  std::vector<Number> sys_soln;
1346 
1347  // Keep track of the variable "offset". This is used for indexing
1348  // into the global solution vector.
1349  unsigned int var_offset = 0;
1350 
1351  // For each system in this EquationSystems object,
1352  // update the global solution and if we are on processor 0,
1353  // loop over the elements and build the nodal solution
1354  // from the element solution. Then insert this nodal solution
1355  // into the vector passed to build_solution_vector.
1356  for (const auto & [sys_name, system] : _systems)
1357  {
1358  // Check current system is listed in system_names, and skip pos if not
1359  bool use_current_system = (system_names == nullptr);
1360  if (!use_current_system)
1361  use_current_system = system_names->count(sys_name);
1362  if (!use_current_system || system->hide_output())
1363  continue;
1364 
1365  const unsigned int nv_sys = system->n_vars();
1366  const auto & dof_map = system->get_dof_map();
1367 
1368  system->update_global_solution (sys_soln, 0);
1369 
1370  // Keep track of the number of vars actually written.
1371  unsigned int n_vars_written_current_system = 0;
1372 
1373  if (_mesh.processor_id() == 0)
1374  {
1375  std::vector<Number> soln_coeffs; // The finite element solution coeffs
1376  std::vector<Number> nodal_soln; // The FE solution interpolated to the nodes
1377  std::vector<dof_id_type> dof_indices; // The DOF indices for the finite element
1378 
1379  // For each variable, determine if we are supposed to
1380  // write it, then loop over the active elements, compute
1381  // the nodal_soln and store it to the "soln" vector. We
1382  // store zeros for subdomain-restricted variables on
1383  // elements where they are not active.
1384  for (unsigned int var=0; var<nv_sys; var++)
1385  {
1386  bool use_current_var = (var_names == nullptr);
1387  if (!use_current_var)
1388  use_current_var = std::count(var_names->begin(),
1389  var_names->end(),
1390  system->variable_name(var));
1391 
1392  // If we aren't supposed to write this var, go to the
1393  // next loop iteration.
1394  if (!use_current_var)
1395  continue;
1396 
1397  const FEType & fe_type = system->variable_type(var);
1398  const Variable & var_description = system->variable(var);
1399  const bool add_p_level = fe_type.p_refinement;
1400 
1401  unsigned int nn=0;
1402 
1403  for (auto & elem : _mesh.active_element_ptr_range())
1404  {
1405  if (var_description.active_on_subdomain(elem->subdomain_id()))
1406  {
1407  dof_map.dof_indices (elem, dof_indices, var);
1408 
1409  soln_coeffs.resize(dof_indices.size());
1410 
1411  for (auto i : index_range(dof_indices))
1412  soln_coeffs[i] = sys_soln[dof_indices[i]];
1413 
1414  // Compute the FE solution at all the nodes, but
1415  // only use the first n_vertices() entries if
1416  // vertices_only == true.
1417  FEInterface::nodal_soln (elem->dim(),
1418  fe_type,
1419  elem,
1420  soln_coeffs,
1421  nodal_soln,
1422  add_p_level,
1423  FEInterface::n_vec_dim(_mesh, fe_type));
1424 
1425  // infinite elements should be skipped...
1426  if (!elem->infinite())
1427  {
1428  libmesh_assert_equal_to (nodal_soln.size(), elem->n_nodes());
1429 
1430  const unsigned int n_vals =
1431  vertices_only ? elem->n_vertices() : elem->n_nodes();
1432 
1433  for (unsigned int n=0; n<n_vals; n++)
1434  {
1435  // Compute index into global solution vector.
1436  std::size_t index =
1437  nv * (nn++) + (n_vars_written_current_system + var_offset);
1438 
1439  soln[index] += nodal_soln[n];
1440  }
1441  }
1442  }
1443  else
1444  nn += vertices_only ? elem->n_vertices() : elem->n_nodes();
1445  } // end loop over active elements writing interiors
1446 
1447  // Loop writing "fake" sides, if requested
1448  if (add_sides)
1449  {
1450  // We don't build discontinuous solution vectors in
1451  // parallel yet, but we'll do ordering of fake side
1452  // values as if we did, for consistency with the
1453  // parallel continuous ordering and for future
1454  // compatibility.
1455  std::vector<std::vector<const Elem *>>
1456  elems_by_pid(_mesh.n_processors());
1457 
1458  for (const auto & elem : _mesh.active_element_ptr_range())
1459  elems_by_pid[elem->processor_id()].push_back(elem);
1460 
1461  for (auto p : index_range(elems_by_pid))
1462  for (const Elem * elem : elems_by_pid[p])
1463  {
1464  if (var_description.active_on_subdomain(elem->subdomain_id()))
1465  {
1466  dof_map.dof_indices (elem, dof_indices, var);
1467 
1468  soln_coeffs.resize(dof_indices.size());
1469 
1470  for (auto i : index_range(dof_indices))
1471  soln_coeffs[i] = sys_soln[dof_indices[i]];
1472 
1473  for (auto s : elem->side_index_range())
1474  {
1475  if (redundant_added_side(*elem,s))
1476  continue;
1477 
1478  const std::vector<unsigned int> side_nodes =
1479  elem->nodes_on_side(s);
1480 
1481  // Compute the FE solution at all the
1482  // side nodes, but only use those for
1483  // which is_vertex() == true if
1484  // vertices_only == true.
1486  (fe_type, elem, s, soln_coeffs,
1487  nodal_soln, add_p_level,
1488  FEInterface::n_vec_dim(_mesh, fe_type));
1489 
1490  libmesh_assert_equal_to
1491  (nodal_soln.size(),
1492  side_nodes.size());
1493 
1494  // If we don't have a continuous FE
1495  // then we want to average between
1496  // sides, at least in the equal-level
1497  // case where it's easy. This is
1498  // analogous to our repeat_count
1499  // behavior elsewhere.
1500  const FEContinuity cont =
1501  FEInterface::get_continuity(fe_type);
1502  const Elem * const neigh = elem->neighbor_ptr(s);
1503 
1504  if ((cont == DISCONTINUOUS || cont == H_CURL || cont == H_DIV) &&
1505  neigh &&
1506  neigh->level() == elem->level() &&
1507  var_description.active_on_subdomain(neigh->subdomain_id()))
1508  {
1509  std::vector<dof_id_type> neigh_indices;
1510  dof_map.dof_indices (neigh, neigh_indices, var);
1511  std::vector<Number> neigh_coeffs(neigh_indices.size());
1512 
1513  for (auto i : index_range(neigh_indices))
1514  neigh_coeffs[i] = sys_soln[neigh_indices[i]];
1515 
1516  const unsigned int s_neigh =
1517  neigh->which_neighbor_am_i(elem);
1518  std::vector<Number> neigh_soln;
1520  (fe_type, neigh, s_neigh,
1521  neigh_coeffs, neigh_soln, add_p_level,
1522  FEInterface::n_vec_dim(_mesh, fe_type));
1523 
1524  const std::vector<unsigned int> neigh_nodes =
1525  neigh->nodes_on_side(s_neigh);
1526  for (auto n : index_range(side_nodes))
1527  for (auto neigh_n : index_range(neigh_nodes))
1528  if (neigh->node_ptr(neigh_nodes[neigh_n])
1529  == elem->node_ptr(side_nodes[n]))
1530  {
1531  nodal_soln[n] += neigh_soln[neigh_n];
1532  nodal_soln[n] /= 2;
1533  }
1534  }
1535 
1536  for (auto n : index_range(side_nodes))
1537  {
1538  if (vertices_only &&
1539  !elem->is_vertex(n))
1540  continue;
1541 
1542  // Compute index into global solution vector.
1543  std::size_t index =
1544  nv * (nn++) + (n_vars_written_current_system + var_offset);
1545 
1546  soln[index] += nodal_soln[n];
1547  }
1548  }
1549  }
1550  else
1551  {
1552  nn += vertices_only ? elem->n_vertices() : elem->n_nodes();
1553 
1554  for (auto s : elem->side_index_range())
1555  {
1556  if (redundant_added_side(*elem,s))
1557  continue;
1558 
1559  const std::vector<unsigned int> side_nodes =
1560  elem->nodes_on_side(s);
1561 
1562  for (auto n : index_range(side_nodes))
1563  {
1564  if (vertices_only &&
1565  !elem->is_vertex(n))
1566  continue;
1567  nn++;
1568  }
1569  }
1570  }
1571  } // end loop over active elements, writing "fake" sides
1572  }
1573  // If we made it here, we actually wrote a variable, so increment
1574  // the number of variables actually written for the current system.
1575  n_vars_written_current_system++;
1576 
1577  } // end loop over vars
1578  } // end if proc 0
1579 
1580  // Update offset for next loop iteration.
1581  var_offset += n_vars_written_current_system;
1582  } // end loop over systems
1583 }
1584 
1585 
1586 
1587 bool EquationSystems::redundant_added_side(const Elem & elem, unsigned int side)
1588 {
1589  libmesh_assert(elem.active());
1590 
1591  const Elem * neigh = elem.neighbor_ptr(side);
1592 
1593  // Write boundary sides.
1594  if (!neigh)
1595  return false;
1596 
1597  // Write ghost sides in Nemesis
1598  if (neigh == remote_elem)
1599  return false;
1600 
1601  // Don't write a coarser side if a finer side exists
1602  if (!neigh->active())
1603  return true;
1604 
1605  // Don't write a side redundantly from both of the
1606  // elements sharing it. We'll disambiguate with id().
1607  return (neigh->id() < elem.id());
1608 }
1609 
1610 
1611 
1613  const Real threshold,
1614  const bool verbose) const
1615 {
1616  // safety check, whether we handle at least the same number
1617  // of systems
1618  std::vector<bool> os_result;
1619 
1620  if (this->n_systems() != other_es.n_systems())
1621  {
1622  if (verbose)
1623  {
1624  libMesh::out << " Fatal difference. This system handles "
1625  << this->n_systems() << " systems," << std::endl
1626  << " while the other system handles "
1627  << other_es.n_systems()
1628  << " systems." << std::endl
1629  << " Aborting comparison." << std::endl;
1630  }
1631  return false;
1632  }
1633  else
1634  {
1635  // start comparing each system
1636  for (const auto & [sys_name, sys_ptr] : _systems)
1637  {
1638  // get the other system
1639  const System & other_system = other_es.get_system (sys_name);
1640 
1641  os_result.push_back (sys_ptr->compare (other_system, threshold, verbose));
1642 
1643  }
1644 
1645  }
1646 
1647 
1648  // sum up the results
1649  if (os_result.size()==0)
1650  return true;
1651  else
1652  {
1653  bool os_identical;
1654  unsigned int n = 0;
1655  do
1656  {
1657  os_identical = os_result[n];
1658  n++;
1659  }
1660  while (os_identical && n<os_result.size());
1661  return os_identical;
1662  }
1663 }
1664 
1665 
1666 
1667 std::string EquationSystems::get_info () const
1668 {
1669  std::ostringstream oss;
1670 
1671  unsigned int n_hidden_sys = 0;
1672  for (auto & pr : _systems)
1673  n_hidden_sys += pr.second->hide_output();
1674 
1675  oss << " EquationSystems\n"
1676  << " n_systems()=" << this->n_systems()
1677  << (n_hidden_sys ? " (hidden: " + std::to_string(n_hidden_sys) + ")" : "")
1678  << "\n";
1679 
1680  // Print the info for the individual systems
1681  for (const auto & pr : _systems)
1682  if (!pr.second->hide_output())
1683  oss << pr.second->get_info();
1684 
1685 
1686  // // Possibly print the parameters
1687  // if (!this->parameters.empty())
1688  // {
1689  // oss << " n_parameters()=" << this->n_parameters() << '\n';
1690  // oss << " Parameters:\n";
1691 
1692  // for (const auto & [key, val] : _parameters)
1693  // oss << " "
1694  // << "\""
1695  // << key
1696  // << "\""
1697  // << "="
1698  // << val
1699  // << '\n';
1700  // }
1701 
1702  return oss.str();
1703 }
1704 
1705 
1706 
1707 void EquationSystems::print_info (std::ostream & os) const
1708 {
1709  os << this->get_info()
1710  << std::endl;
1711 }
1712 
1713 
1714 
1715 std::ostream & operator << (std::ostream & os,
1716  const EquationSystems & es)
1717 {
1718  es.print_info(os);
1719  return os;
1720 }
1721 
1722 
1723 
1724 unsigned int EquationSystems::n_vars () const
1725 {
1726  unsigned int tot=0;
1727 
1728  for (const auto & pr : _systems)
1729  tot += pr.second->n_vars();
1730 
1731  return tot;
1732 }
1733 
1734 
1735 
1736 std::size_t EquationSystems::n_dofs () const
1737 {
1738  std::size_t tot=0;
1739 
1740  for (const auto & pr : _systems)
1741  tot += pr.second->n_dofs();
1742 
1743  return tot;
1744 }
1745 
1746 
1747 
1748 
1750 {
1751  std::size_t tot=0;
1752 
1753  for (const auto & pr : _systems)
1754  tot += pr.second->n_active_dofs();
1755 
1756  return tot;
1757 }
1758 
1759 
1761 {
1762  // All the nodes
1763  for (auto & node : _mesh.node_ptr_range())
1764  node->add_system();
1765 
1766  // All the elements
1769  [](const ElemRange & range)
1770  {
1771  for (Elem * elem : range)
1772  elem->add_system();
1773  });
1774 }
1775 
1777 {
1778  this->get_system(sys_num).get_dof_map().remove_default_ghosting();
1779 }
1780 
1781 } // namespace libMesh
EquationSystems(MeshBase &mesh)
Constructor.
std::string name(const ElemQuality q)
This function returns a string containing some name for q.
Definition: elem_quality.C:42
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
Definition: fe_type.h:196
void parallel_for(const Range &range, const Body &body, unsigned int n_threads=libMesh::n_threads())
Execute the provided function object in parallel on the specified range.
Definition: threads_none.h:73
void allgather(const T &send_data, std::vector< T, A > &recv_data) const
bool p_refinement
Whether or not the finite elements for this type increase their p refinement level on geometric eleme...
Definition: fe_type.h:292
This is the EquationSystems class.
unsigned int n_vars() const
void build_variable_names(std::vector< std::string > &var_names, const FEType *type=nullptr, const std::set< std::string > *system_names=nullptr) const
Fill the input vector var_names with the names of the variables for each system.
A Node is like a Point, but with more information.
Definition: node.h:52
unsigned int n_systems() const
const Variable & variable(unsigned int var) const
Return a constant reference to Variable var.
Definition: system.C:2704
bool _enable_default_ghosting
Flag for whether to enable default ghosting on newly added Systems.
virtual void get(const std::vector< numeric_index_type > &index, T *values) const
Access multiple components at once.
Data structure for specifying which Parameters should be independent variables in a parameter sensiti...
void dof_indices(const Elem *const elem, std::vector< dof_id_type > &di) const
Definition: dof_map.C:2201
std::unique_ptr< NumericVector< Number > > build_parallel_elemental_solution_vector(std::vector< std::string > &names) const
Builds a parallel vector of CONSTANT MONOMIAL and/or components of CONSTANT MONOMIAL_VEC solution val...
std::size_t distribute_dofs(MeshBase &)
Distribute dofs on the current mesh.
Definition: dof_map.C:949
static constexpr Real TOLERANCE
std::size_t n_dofs() const
Data structure for specifying which Quantities of Interest should be calculated in an adjoint or a pa...
Definition: qoi_set.h:45
unsigned int dim
void build_discontinuous_solution_vector(std::vector< Number > &soln, const std::set< std::string > *system_names=nullptr, const std::vector< std::string > *var_names=nullptr, bool vertices_only=false, bool add_sides=false) const
Fill the input vector soln with solution values.
void add_default_ghosting()
Add the default functor(s) for coupling and algebraic ghosting.
Definition: dof_map.C:1996
bool refine_elements()
Only refines the user-requested elements.
bool reinit_solutions()
Handle any mesh changes and project any solutions onto the updated mesh.
virtual void clear()
Restores the data structure to a pristine state.
virtual void allgather()
Gathers all elements and nodes of the mesh onto every processor.
Definition: mesh_base.h:376
virtual ~EquationSystems()
Destructor.
void sum(T &r) const
This is the base class from which all geometric element types are derived.
Definition: elem.h:94
void print_info(std::ostream &os=libMesh::out) const
Prints information about the equation systems, by default to libMesh::out.
MeshBase & mesh
static FEFieldType field_type(const FEType &fe_type)
std::ostream & operator<<(std::ostream &os, const OrderWrapper &order)
Overload stream operators.
Definition: fe_type.h:182
processor_id_type rank() const
const Parallel::Communicator & comm() const
virtual void enable_default_ghosting(bool enable)
Enable or disable default ghosting functors on the Mesh and on all Systems.
virtual void adjoint_solve(const QoISet &qoi_indices=QoISet())
Call adjoint_solve on all the individual equation systems.
The StoredRange class defines a contiguous, divisible set of objects.
Definition: stored_range.h:54
const Parallel::Communicator & _communicator
The libMesh namespace provides an interface to certain functionality in the library.
const T_sys & get_system(std::string_view name) const
Real distance(const Point &p)
virtual void init(const numeric_index_type n, const numeric_index_type n_local, const bool fast=false, const ParallelType ptype=AUTOMATIC)=0
Change the dimension of the vector to n.
static void side_nodal_soln(const FEType &fe_t, const Elem *elem, const unsigned int side, const std::vector< Number > &elem_soln, std::vector< Number > &nodal_soln, const bool add_p_level=true, const unsigned int vdim=1)
Build the nodal soln on one side from the (full) element soln.
Definition: fe_interface.C:651
uint8_t processor_id_type
Definition: id_types.h:104
This is the MeshBase class.
Definition: mesh_base.h:80
virtual std::string get_info() const
virtual void prolong_vectors()
Prolong vectors after the mesh has refined.
Definition: system.C:432
MeshBase & _mesh
The mesh data structure.
bool coarsen_elements()
Only coarsens the user-requested elements.
signed char & underrefined_boundary_limit()
If underrefined_boundary_limit is set to a nonnegative value, then refinement and coarsening will pro...
unsigned int n_dofs(const unsigned int s, const unsigned int var=libMesh::invalid_uint) const
Definition: dof_object.h:797
This class handles the numbering of degrees of freedom on a mesh.
Definition: dof_map.h:179
void allgather()
Serializes a distributed mesh and its associated degree of freedom numbering for all systems...
void get_vars_active_subdomains(const std::vector< std::string > &names, std::vector< std::set< subdomain_id_type >> &vars_active_subdomains) const
Retrieve vars_active_subdomains, which indicates the active subdomains for each variable in names...
processor_id_type size() const
virtual void sensitivity_solve(const ParameterVector &parameters)
Call sensitivity_solve on all the individual equation systems.
virtual void reinit_systems()
Reinitialize all systems on the current mesh.
processor_id_type n_processors() const
virtual bool is_serial() const
Definition: mesh_base.h:347
unsigned int number() const
Definition: system.h:2393
This class defines the notion of a variable in the system.
Definition: variable.h:50
const std::set< subdomain_id_type > & active_subdomains() const
Definition: variable.h:181
Implements (adaptive) mesh refinement algorithms for a MeshBase.
dof_id_type id() const
Definition: dof_object.h:819
dof_id_type numeric_index_type
Definition: id_types.h:99
const ElemRange & element_stored_range()
Definition: mesh_base.C:1913
unsigned int which_neighbor_am_i(const Elem *e) const
This function tells you which neighbor e is.
Definition: elem.h:2933
Manages consistently variables, degrees of freedom, and coefficient vectors.
Definition: system.h:98
virtual void reinit()
Handle any mesh changes and reinitialize all the systems on the updated mesh.
virtual dof_id_type max_elem_id() const =0
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
Definition: system.h:1655
libmesh_assert(ctx)
const ConstElemRange & active_local_element_stored_range() const
Definition: mesh_base.C:1928
const std::string & variable_name(const unsigned int i) const
Definition: system.C:2679
void build_solution_vector(std::vector< Number > &soln, std::string_view system_name, std::string_view variable_name="all_vars") const
Fill the input vector soln with the solution values for the system named name.
virtual void restrict_vectors()
Restrict vectors after the mesh has coarsened.
Definition: system.C:374
bool active_on_subdomain(subdomain_id_type sid) const
Definition: variable.h:167
bool _refine_in_reinit
Flag for whether to call coarsen/refine in reinit().
virtual void reinit_mesh()
Handle the association of a completely new mesh with the EquationSystem and all the Systems assigned ...
unsigned char & face_level_mismatch_limit()
If face_level_mismatch_limit is set to a nonzero value, then refinement and coarsening will produce m...
An object whose state is distributed along a set of processors.
static unsigned int n_vec_dim(const MeshBase &mesh, const FEType &fe_type)
virtual void reinit_constraints()
Reinitializes the constraints for this system.
Definition: system.C:483
virtual void close()=0
Calls the NumericVector&#39;s internal assembly routines, ensuring that the values are consistent across ...
static FEContinuity get_continuity(const FEType &fe_type)
Returns the input FEType&#39;s FEContinuity based on the underlying FEFamily and potentially the Order...
virtual void solve()
Call solve on all the individual equation systems.
virtual numeric_index_type first_local_index() const =0
const Elem * neighbor_ptr(unsigned int i) const
Definition: elem.h:2612
virtual void update()
Update the local values to reflect the solution on neighboring processors.
Definition: system.C:498
unsigned int level() const
Definition: elem.h:3088
const FEType & variable_type(const unsigned int i) const
Definition: system.C:2721
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual bool contract()=0
Delete subactive (i.e.
subdomain_id_type subdomain_id() const
Definition: elem.h:2588
FEContinuity
defines an enum for finite element types to libmesh_assert a certain level (or type? Hcurl?) of continuity.
const Node * node_ptr(const unsigned int i) const
Definition: elem.h:2513
unsigned int spatial_dimension() const
Definition: mesh_base.C:606
T & set(const std::string &)
Definition: parameters.h:494
std::unique_ptr< NumericVector< Number > > build_parallel_solution_vector(const std::set< std::string > *system_names=nullptr, bool add_sides=false) const
A version of build_solution_vector which is appropriate for "parallel" output formats like Nemesis...
OStreamProxy out
virtual void clear()
Clears internal data structures & frees any allocated memory.
Definition: parameters.h:349
signed char & overrefined_boundary_limit()
If overrefined_boundary_limit is set to a nonnegative value, then refinement and coarsening will prod...
void remove_default_ghosting()
Remove any default ghosting functor(s).
Definition: dof_map.C:1988
void _remove_default_ghosting(unsigned int sys_num)
This just calls DofMap::remove_default_ghosting() but using a shim lets us forward-declare DofMap...
const MeshBase & get_mesh() const
std::size_t n_active_dofs() const
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition: int_range.h:176
static std::unique_ptr< NumericVector< T > > build(const Parallel::Communicator &comm, SolverPackage solver_package=libMesh::default_solver_package(), ParallelType parallel_type=AUTOMATIC)
Builds a NumericVector on the processors in communicator comm using the linear solver package specifi...
std::vector< std::pair< unsigned int, unsigned int > > find_variable_numbers(std::vector< std::string > &names, const FEType *type=nullptr, const std::vector< FEType > *types=nullptr) const
Finds system and variable numbers for any variables of &#39;type&#39; or of &#39;types&#39; corresponding to the entr...
Parameters parameters
Data structure holding arbitrary parameters.
std::unique_ptr< NumericVector< Number > > current_local_solution
All the values I need to compute my contribution to the simulation at hand.
Definition: system.h:1667
virtual void set(const numeric_index_type i, const T value)=0
Sets v(i) = value.
virtual void init()
Initialize all the systems.
virtual bool compare(const EquationSystems &other_es, const Real threshold, const bool verbose) const
unsigned int n_vars() const
Definition: system.C:2674
virtual dof_id_type max_node_id() const =0
virtual dof_id_type n_elem() const =0
virtual void add(const numeric_index_type i, const T value)=0
Adds value to the vector entry specified by i.
virtual System & add_system(std::string_view system_type, std::string_view name)
Add the system of type system_type named name to the systems array.
processor_id_type processor_id() const
static void nodal_soln(const unsigned int dim, const FEType &fe_t, const Elem *elem, const std::vector< Number > &elem_soln, std::vector< Number > &nodal_soln, const bool add_p_level=true, const unsigned int vdim=1)
Build the nodal soln from the element soln.
Definition: fe_interface.C:626
bool active() const
Definition: elem.h:2955
void _add_system_to_nodes_and_elems()
This function is used in the implementation of add_system, it loops over the nodes and elements of th...
const DofMap & get_dof_map() const
Definition: system.h:2417
virtual numeric_index_type last_local_index() const =0
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition: int_range.h:153
void update()
Updates local values for all the systems.
virtual std::vector< unsigned int > nodes_on_side(const unsigned int) const =0
void build_elemental_solution_vector(std::vector< Number > &soln, std::vector< std::string > &names) const
Retrieve the solution data for CONSTANT MONOMIALs and/or components of CONSTANT MONOMIAL_VECs.
uint8_t dof_id_type
Definition: id_types.h:67
std::map< std::string, std::unique_ptr< System >, std::less<> > _systems
Data structure holding the systems.
static bool redundant_added_side(const Elem &elem, unsigned int side)
const RemoteElem * remote_elem
Definition: remote_elem.C:57