libMesh
Loading...
Searching...
No Matches
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
49namespace libMesh
50{
51
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
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
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{
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
353System & 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
455void 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
465void 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
583{
584 return type.order == CONSTANT &&
585 (type.family == MONOMIAL ||
586 type.family == MONOMIAL_VEC ||
587 type.family == XYZ);
588}
589
590
591
593 (std::vector<std::string> & var_names,
594 const std::set<std::string> * system_names) const
595{
596 const std::vector<std::string> name_filter = var_names;
597 const bool is_names_empty = name_filter.empty();
598 var_names.clear();
599
600 const std::vector<std::string> component_suffix = {"_x", "_y", "_z"};
601 const unsigned int dim = _mesh.spatial_dimension();
602 libmesh_error_msg_if(dim > 3, "Invalid dim in build_elemental_data_variable_names");
603
604 for (const auto & [sys_name, sys_ptr] : _systems)
605 {
606 const bool use_current_system = (system_names == nullptr) || system_names->count(sys_name);
607 if (!use_current_system || sys_ptr->hide_output())
608 continue;
609
610 for (auto var : make_range(sys_ptr->n_vars()))
611 {
612 const FEType & var_type = sys_ptr->variable_type(var);
614 continue;
615
616 if (FEInterface::field_type(var_type) == TYPE_VECTOR)
617 {
618 for (auto comp : make_range(dim))
619 {
620 const std::string name =
621 sys_ptr->variable_name(var) + component_suffix[comp];
622
623 if (is_names_empty ||
624 std::find(name_filter.begin(), name_filter.end(), name) != name_filter.end())
625 var_names.push_back(name);
626 }
627 }
628 else
629 {
630 const std::string & name = sys_ptr->variable_name(var);
631
632 if (is_names_empty ||
633 std::find(name_filter.begin(), name_filter.end(), name) != name_filter.end())
634 var_names.push_back(name);
635 }
636 }
637 }
638
639 std::sort(var_names.begin(), var_names.end());
640}
641
642
643
644void EquationSystems::build_solution_vector (std::vector<Number> &,
645 std::string_view,
646 std::string_view) const
647{
648 // TODO:[BSK] re-implement this from the method below
649 libmesh_not_implemented();
650}
651
652
653
654
655std::unique_ptr<NumericVector<Number>>
656EquationSystems::build_parallel_solution_vector(const std::set<std::string> * system_names,
657 bool add_sides) const
658{
659 LOG_SCOPE("build_parallel_solution_vector()", "EquationSystems");
660
661 // This function must be run on all processors at once
662 parallel_object_only();
663
664 const unsigned int dim = _mesh.spatial_dimension();
665 const dof_id_type max_nn = _mesh.max_node_id();
666
667 // allocate vector storage to hold
668 // (max_node_id)*(number_of_variables) entries.
669 //
670 // If node renumbering is disabled and adaptive coarsening has
671 // created gaps between node numbers, then this vector will be
672 // sparse.
673 //
674 // We have to differentiate between between scalar and vector
675 // variables. We intercept vector variables and treat each
676 // component as a scalar variable (consistently with build_solution_names).
677
678 unsigned int nv = 0;
679
680 //Could this be replaced by a/some convenience methods?[PB]
681 {
682 unsigned int n_scalar_vars = 0;
683 unsigned int n_vector_vars = 0;
684 for (const auto & [sys_name, sys_ptr] : _systems)
685 {
686 // Check current system is listed in system_names, and skip pos if not
687 bool use_current_system = (system_names == nullptr);
688 if (!use_current_system)
689 use_current_system = system_names->count(sys_name);
690 if (!use_current_system || sys_ptr->hide_output())
691 continue;
692
693 for (auto vn : make_range(sys_ptr->n_vars()))
694 {
695 if (FEInterface::field_type(sys_ptr->variable_type(vn)) == TYPE_VECTOR)
696 n_vector_vars++;
697 else
698 n_scalar_vars++;
699 }
700 }
701 // Here, we're assuming the number of vector components is the same
702 // as the mesh spatial dimension.
703 nv = n_scalar_vars + dim*n_vector_vars;
704 }
705
706 // Get the number of nodes to store locally.
707 dof_id_type n_local_nodes = cast_int<dof_id_type>
708 (std::distance(_mesh.local_nodes_begin(),
709 _mesh.local_nodes_end()));
710
711 // If node renumbering has been disabled, nodes may not be numbered
712 // contiguously, and the number of nodes might not match the
713 // max_node_id. In this case we just do our best.
714 dof_id_type n_total_nodes = n_local_nodes;
715 _mesh.comm().sum(n_total_nodes);
716
717 const processor_id_type n_proc = _mesh.comm().size();
718 const processor_id_type my_pid = _mesh.comm().rank();
719 const dof_id_type n_gaps = max_nn - n_total_nodes;
720 const dof_id_type gaps_per_processor = n_gaps / n_proc;
721 const dof_id_type remainder_gaps = n_gaps % n_proc;
722
723 n_local_nodes = n_local_nodes + // Actual nodes
724 gaps_per_processor + // Our even share of gaps
725 (my_pid < remainder_gaps); // Leftovers
726
727 // If we've been asked to build added sides' data, we need space to
728 // add it. Keep track of how much space.
729 dof_id_type local_added_side_nodes = 0,
730 added_side_nodes = 0;
731
732 // others_added_side_nodes[p]: local_added_side_nodes on rank p
733 std::vector<dof_id_type> others_added_side_nodes;
734
735 // A map of (element_id, side, side_node) pairs to the corresponding
736 // added side node index.
737 std::map<std::tuple<dof_id_type, unsigned short, unsigned short>,
738 dof_id_type> discontinuous_node_indices;
739
740 // If we don't have any added side nodes, we'll have no offsets from
741 // them, and we won't care about which offsets apply to which node
742 // ids either.
743
744 // Number of true nodes on processors [0,p]
745 std::vector<dof_id_type> true_node_offsets;
746 // Number of added (fake) nodes on processors [0,p)
747 std::vector<dof_id_type> added_node_offsets;
748
749 auto node_id_to_vec_id =
750 [&true_node_offsets, &added_node_offsets]
751 (const dof_id_type node_id)
752 {
753 if (true_node_offsets.empty())
754 return node_id; // O(1) in the common !add_sides case
755
756 // Find the processor id that has node_id in the parallel vec
757 const auto lb = std::upper_bound(true_node_offsets.begin(),
758 true_node_offsets.end(), node_id);
759 libmesh_assert(lb != true_node_offsets.end());
760 const processor_id_type p = lb - true_node_offsets.begin();
761
762 return node_id + added_node_offsets[p];
763 };
764
765 if (add_sides)
766 {
767 true_node_offsets.resize(n_proc);
768 added_node_offsets.resize(n_proc);
769
770 // One loop to count everyone's new side nodes
771 for (const auto & elem : _mesh.active_element_ptr_range())
772 {
773 for (auto s : elem->side_index_range())
774 {
775 if (redundant_added_side(*elem,s))
776 continue;
777
778 const std::vector<unsigned int> side_nodes =
779 elem->nodes_on_side(s);
780
781 if (elem->processor_id() == this->processor_id())
782 local_added_side_nodes += side_nodes.size();
783 }
784 }
785
786 others_added_side_nodes.resize(n_proc);
787 _mesh.comm().allgather(local_added_side_nodes,
788 others_added_side_nodes);
789
790 added_side_nodes = std::accumulate(others_added_side_nodes.begin(),
791 others_added_side_nodes.end(), 0,
792 std::plus<>());
793
794 _mesh.comm().allgather(n_local_nodes, true_node_offsets);
795 for (auto p : make_range(n_proc-1))
796 true_node_offsets[p+1] += true_node_offsets[p];
797 libmesh_assert_equal_to(true_node_offsets[n_proc-1], _mesh.max_node_id());
798
799 // For nodes that exist in the mesh, we just need an offset to
800 // tell where to put their solutions.
801 added_node_offsets[0] = 0;
802 for (auto p : make_range(n_proc-1))
803 added_node_offsets[p+1] =
804 added_node_offsets[p] + others_added_side_nodes[p];
805
806 // For added side nodes, we need to fill a map. Start after all
807 // the true node for our pid plus all the side nodes for
808 // previous pids
809 dof_id_type node_counter = true_node_offsets[my_pid];
810 for (auto p : make_range(my_pid))
811 node_counter += others_added_side_nodes[p];
812
813 // One loop to figure out whose added side nodes get which index
814 for (const auto & elem : _mesh.active_local_element_ptr_range())
815 {
816 for (auto s : elem->side_index_range())
817 {
818 if (redundant_added_side(*elem,s))
819 continue;
820
821 const std::vector<unsigned int> side_nodes =
822 elem->nodes_on_side(s);
823
824 for (auto n : index_range(side_nodes))
825 discontinuous_node_indices
826 [std::make_tuple(elem->id(),s,n)] = node_counter++;
827 }
828 }
829 }
830
831 const dof_id_type
832 n_global_vals = (max_nn + added_side_nodes) * nv,
833 n_local_vals = (n_local_nodes + local_added_side_nodes) * nv;
834
835 // Create a NumericVector to hold the parallel solution
836 std::unique_ptr<NumericVector<Number>> parallel_soln_ptr = NumericVector<Number>::build(_communicator);
837 NumericVector<Number> & parallel_soln = *parallel_soln_ptr;
838 parallel_soln.init(n_global_vals, n_local_vals, false, PARALLEL);
839
840 // Create a NumericVector to hold the "repeat_count" for each node - this is essentially
841 // the number of elements contributing to that node's value
842 std::unique_ptr<NumericVector<Number>> repeat_count_ptr = NumericVector<Number>::build(_communicator);
843 NumericVector<Number> & repeat_count = *repeat_count_ptr;
844 repeat_count.init(n_global_vals, n_local_vals, false, PARALLEL);
845
846 repeat_count.close();
847
848 unsigned int var_num=0;
849
850 // For each system in this EquationSystems object,
851 // update the global solution and if we are on processor 0,
852 // loop over the elements and build the nodal solution
853 // from the element solution. Then insert this nodal solution
854 // into the vector passed to build_solution_vector.
855 for (const auto & [sys_name, sys_ptr] : _systems)
856 {
857 // Check current system is listed in system_names, and skip pos if not
858 bool use_current_system = (system_names == nullptr);
859 if (!use_current_system)
860 use_current_system = system_names->count(sys_name);
861 if (!use_current_system || sys_ptr->hide_output())
862 continue;
863
864 const System & system = *sys_ptr;
865 const unsigned int nv_sys = system.n_vars();
866 const unsigned int sys_num = system.number();
867
868 //Could this be replaced by a/some convenience methods?[PB]
869 unsigned int n_scalar_vars = 0;
870 unsigned int n_vector_vars = 0;
871 for (auto vn : make_range(sys_ptr->n_vars()))
872 {
873 if (FEInterface::field_type(sys_ptr->variable_type(vn)) == TYPE_VECTOR)
874 n_vector_vars++;
875 else
876 n_scalar_vars++;
877 }
878
879 // Here, we're assuming the number of vector components is the same
880 // as the mesh spatial dimension.
881 unsigned int nv_sys_split = n_scalar_vars + dim*n_vector_vars;
882
883 // Update the current_local_solution
884 {
885 System & non_const_sys = const_cast<System &>(system);
886 // We used to simply call non_const_sys.solution->close()
887 // here, but that is not allowed when the solution vector is
888 // locked read-only, for example when printing the solution
889 // during the middle of a solve... So try to be a bit
890 // more careful about calling close() unnecessarily.
891 libmesh_assert(this->comm().verify(non_const_sys.solution->closed()));
892 if (!non_const_sys.solution->closed())
893 non_const_sys.solution->close();
894 non_const_sys.update();
895 }
896
898
899 const DofMap & dof_map = system.get_dof_map();
900
901 std::vector<Number> elem_soln; // The finite element solution
902 std::vector<Number> nodal_soln; // The FE solution interpolated to the nodes
903 std::vector<dof_id_type> dof_indices; // The DOF indices for the finite element
904
905 unsigned var_inc = 0;
906 for (unsigned int var=0; var<nv_sys; var++)
907 {
908 const FEType & fe_type = system.variable_type(var);
909 const Variable & var_description = system.variable(var);
910 unsigned int n_vec_dim = FEInterface::n_vec_dim( sys_ptr->get_mesh(), fe_type );
911 const bool add_p_level = fe_type.p_refinement;
912
913 for (const auto & elem : _mesh.active_local_element_ptr_range())
914 {
915 if (var_description.active_on_subdomain(elem->subdomain_id()))
916 {
917 dof_map.dof_indices (elem, dof_indices, var);
918 sys_soln.get(dof_indices, elem_soln);
919
920 FEInterface::nodal_soln (elem->dim(),
921 fe_type,
922 elem,
923 elem_soln,
924 nodal_soln,
925 add_p_level,
926 n_vec_dim);
927
928 // infinite elements should be skipped...
929 if (!elem->infinite())
930 {
931 libmesh_assert_equal_to (nodal_soln.size(), n_vec_dim*elem->n_nodes());
932
933 for (auto n : elem->node_index_range())
934 {
935 const Node & node = elem->node_ref(n);
936
937 const dof_id_type node_idx =
938 nv * node_id_to_vec_id(node.id());
939
940 for (unsigned int d=0; d < n_vec_dim; d++)
941 {
942 // For vector-valued elements, all components are in nodal_soln. For each
943 // node, the components are stored in order, i.e. node_0 -> s0_x, s0_y, s0_z
944 parallel_soln.add(node_idx + (var_inc+d + var_num), nodal_soln[n_vec_dim*n+d]);
945
946 // Increment the repeat count for this position
947 repeat_count.add(node_idx + (var_inc+d + var_num), 1);
948 }
949 }
950
951 if (add_sides)
952 {
953 for (auto s : elem->side_index_range())
954 {
955 if (redundant_added_side(*elem,s))
956 continue;
957
958 // Compute the FE solution at all the
959 // side nodes
961 (fe_type, elem, s, elem_soln,
962 nodal_soln, add_p_level, n_vec_dim);
963
964#ifdef DEBUG
965 const std::vector<unsigned int> side_nodes =
966 elem->nodes_on_side(s);
967
968 libmesh_assert_equal_to
969 (nodal_soln.size(),
970 side_nodes.size());
971#endif
972
973 for (auto n : index_range(nodal_soln))
974 {
975 // Retrieve index into global solution vector.
976 std::size_t node_index =
977 nv * libmesh_map_find(discontinuous_node_indices,
978 std::make_tuple(elem->id(), s, n));
979
980 for (unsigned int d=0; d < n_vec_dim; d++)
981 {
982 parallel_soln.add(node_index + (var_inc+d + var_num), nodal_soln[n_vec_dim*n+d]);
983 repeat_count.add(node_index + (var_inc+d + var_num), 1);
984 }
985 }
986 }
987 }
988 }
989 }
990 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:
991 for (auto n : elem->node_index_range())
992 {
993 const Node & node = elem->node_ref(n);
994 // Only do this if this variable has NO DoFs at
995 // this node... it might have some from an
996 // adjoining element...
997 if (!node.n_dofs(sys_num, var))
998 {
999 const dof_id_type node_idx =
1000 nv * node_id_to_vec_id(node.id());
1001
1002 for (unsigned int d=0; d < n_vec_dim; d++)
1003 repeat_count.add(node_idx + (var_inc+d + var_num), 1);
1004 }
1005 }
1006
1007 } // end loop over elements
1008 var_inc += n_vec_dim;
1009 } // end loop on variables in this system
1010
1011 var_num += nv_sys_split;
1012 } // end loop over systems
1013
1014 // Sum the nodal solution values and repeat counts.
1015 parallel_soln.close();
1016 repeat_count.close();
1017
1018 // If there were gaps in the node numbering, there will be
1019 // corresponding zeros in the parallel_soln and repeat_count
1020 // vectors. We need to set those repeat_count entries to 1
1021 // in order to avoid dividing by zero.
1022 if (n_gaps)
1023 {
1024 for (numeric_index_type i=repeat_count.first_local_index();
1025 i<repeat_count.last_local_index(); ++i)
1026 {
1027 // repeat_count entries are integral values but let's avoid a
1028 // direct floating point comparison with 0 just in case some
1029 // roundoff noise crept in during vector assembly?
1030 if (std::abs(repeat_count(i)) < TOLERANCE)
1031 repeat_count.set(i, 1.);
1032 }
1033
1034 // Make sure the repeat_count vector is up-to-date on all
1035 // processors.
1036 repeat_count.close();
1037 }
1038
1039 // Divide to get the average value at the nodes
1040 parallel_soln /= repeat_count;
1041
1042 return parallel_soln_ptr;
1043}
1044
1045
1046
1047void EquationSystems::build_solution_vector (std::vector<Number> & soln,
1048 const std::set<std::string> * system_names,
1049 bool add_sides) const
1050{
1051 LOG_SCOPE("build_solution_vector()", "EquationSystems");
1052
1053 // Call the parallel implementation
1054 std::unique_ptr<NumericVector<Number>> parallel_soln =
1055 this->build_parallel_solution_vector(system_names, add_sides);
1056
1057 // Localize the NumericVector into the provided std::vector.
1058 parallel_soln->localize_to_one(soln);
1059}
1060
1061
1062
1063void EquationSystems::get_vars_active_subdomains(const std::vector<std::string> & names,
1064 std::vector<std::set<subdomain_id_type>> & vars_active_subdomains) const
1065{
1066 vars_active_subdomains.clear();
1067 vars_active_subdomains.resize(names.size());
1068
1069 for (const auto & pr : _systems)
1070 {
1071 const auto & sys_ptr = pr.second;
1072 for (auto vn : make_range(sys_ptr->n_vars()))
1073 {
1074 const std::string & var_name = sys_ptr->variable_name(vn);
1075
1076 auto names_it = std::find(names.begin(), names.end(), var_name);
1077 if(names_it != names.end())
1078 {
1079 const Variable & variable = sys_ptr->variable(vn);
1080 const std::set<subdomain_id_type> & active_subdomains = variable.active_subdomains();
1081 vars_active_subdomains[std::distance(names.begin(), names_it)] = active_subdomains;
1082 }
1083 }
1084 }
1085}
1086
1087
1088
1089void
1091 std::vector<std::string> & names) const
1092{
1093 // Call the parallel version of this function
1094 std::unique_ptr<NumericVector<Number>> parallel_soln =
1096
1097 // Localize into 'soln', provided that parallel_soln is not empty.
1098 // Note: parallel_soln will be empty in the event that none of the
1099 // input names were elemental data variables, or there were simply none of these in
1100 // the EquationSystems object.
1101 soln.clear();
1102 if (parallel_soln)
1103 parallel_soln->localize_to_one(soln);
1104}
1105
1106std::vector<std::pair<unsigned int, unsigned int>>
1108 (std::vector<std::string> & names, const FEType * type, const std::vector<FEType> * types) const
1109{
1110 // Resolve class of type input and assert that at least one of them is null
1111 libmesh_assert_msg(!type || !types,
1112 "Input 'type', 'types', or neither in find_variable_numbers, but not both.");
1113
1114 std::vector<FEType> type_filter;
1115 if (type)
1116 type_filter.push_back(*type);
1117 else if (types)
1118 type_filter = *types;
1119
1121 (names,
1122 [&type_filter](const FEType & var_type)
1123 {
1124 return type_filter.empty() ||
1125 std::find(type_filter.begin(), type_filter.end(), var_type) != type_filter.end();
1126 });
1127}
1128
1129
1130
1131std::vector<std::pair<unsigned int, unsigned int>>
1137
1138
1139
1140std::vector<std::pair<unsigned int, unsigned int>>
1142 (std::vector<std::string> & names,
1143 const std::function<bool(const FEType &)> & type_filter) const
1144{
1145 // This function must be run on all processors at once
1146 parallel_object_only();
1147
1148 libmesh_assert (this->n_systems());
1149
1150 // Store a copy of the valid variable names, if any. The names vector will be repopulated with any
1151 // valid names (or all if 'is_names_empty') in the system that passes through the type filter. If
1152 // the variable is a vector, its name will be decomposed into its separate components in
1153 // accordance with build_variable_names().
1154 std::vector<std::string> name_filter = names;
1155 bool is_names_empty = name_filter.empty();
1156 names.clear();
1157
1158 // initialize convenience variables
1159 FEType var_type;
1160 std::string name;
1161
1162 const std::vector<std::string> component_suffix = {"_x", "_y", "_z"};
1163 unsigned int dim = _mesh.spatial_dimension();
1164 libmesh_error_msg_if(dim > 3, "Invalid dim in find_variable_numbers");
1165
1166 // Now filter through the variables in each system and store the system index and their index
1167 // within that system. This way, we know where to find their data even after we sort them.
1168 std::vector<std::pair<unsigned int, unsigned int>> var_nums;
1169
1170 for (const auto & pr : _systems)
1171 {
1172 const System & system = *(pr.second);
1173
1174 for (auto var : make_range(system.n_vars()))
1175 {
1176 // apply the type filter
1177 var_type = system.variable_type(var);
1178 if (!type_filter(var_type))
1179 continue;
1180
1181 // apply the name filter (note that all variables pass if it is empty)
1182 if (FEInterface::field_type(var_type) == TYPE_VECTOR)
1183 {
1184 std::vector<std::string> component_names;
1185 for (unsigned int comp = 0; comp < dim; ++comp)
1186 {
1187 name = system.variable_name(var) + component_suffix[comp];
1188 if (is_names_empty ||
1189 (std::find(name_filter.begin(), name_filter.end(), name) != name_filter.end()))
1190 component_names.push_back(name);
1191 }
1192
1193 if (! component_names.empty())
1194 names.insert(names.end(), component_names.begin(), component_names.end());
1195 else
1196 continue;
1197 }
1198 else /*scalar-valued variable*/
1199 {
1200 name = system.variable_name(var);
1201 if (is_names_empty ||
1202 (std::find(name_filter.begin(), name_filter.end(), name) != name_filter.end()))
1203 names.push_back(name);
1204 else
1205 continue;
1206 }
1207
1208 // if the variable made it through both filters get its system indices
1209 var_nums.emplace_back(system.number(), var);
1210 }
1211 }
1212
1213 // Sort the var_nums vector pairs alphabetically based on the variable name
1214 std::vector<unsigned int> sort_index(var_nums.size());
1215 std::iota(sort_index.begin(), sort_index.end(), 0);
1216 std::sort(sort_index.begin(), sort_index.end(),
1217 [&](const unsigned int & lhs, const unsigned int & rhs)
1218 {return this->get_system(var_nums[lhs].first).variable_name(var_nums[lhs].second) <
1219 this->get_system(var_nums[rhs].first).variable_name(var_nums[rhs].second);});
1220
1221 std::vector<std::pair<unsigned int, unsigned int>> var_nums_sorted(var_nums.size());
1222 for (auto i : index_range(var_nums_sorted))
1223 {
1224 var_nums_sorted[i].first = var_nums[sort_index[i]].first;
1225 var_nums_sorted[i].second = var_nums[sort_index[i]].second;
1226 }
1227
1228 // Also sort the names vector
1229 std::sort(names.begin(), names.end());
1230
1231 // Return the sorted vector pairs
1232 return var_nums_sorted;
1233}
1234
1235
1236std::unique_ptr<NumericVector<Number>>
1238{
1239 // Filter any names that aren't elemental variables and get the system indices for those that are.
1240 // Note that it's probably fine if the names vector is empty since we'll still filter out all
1241 // non-elemental-data variables. If there are none, then nothing is output here.
1242 std::vector<std::pair<unsigned int, unsigned int>> var_nums =
1244
1245 const std::size_t nv = names.size(); /*total number of vars including vector components*/
1246 const dof_id_type ne = _mesh.n_elem();
1247 libmesh_assert_equal_to (ne, _mesh.max_elem_id());
1248
1249 // If there are no variables to write out don't do anything...
1250 if (!nv)
1251 return std::unique_ptr<NumericVector<Number>>(nullptr);
1252
1253 // We can handle the case where there are nullptrs in the Elem vector
1254 // by just having extra zeros in the solution vector.
1255 numeric_index_type parallel_soln_global_size = ne*nv;
1256
1257 numeric_index_type div = parallel_soln_global_size / this->n_processors();
1258 numeric_index_type mod = parallel_soln_global_size % this->n_processors();
1259
1260 // Initialize all processors to the average size.
1261 numeric_index_type parallel_soln_local_size = div;
1262
1263 // The first "mod" processors get an extra entry.
1264 if (this->processor_id() < mod)
1265 parallel_soln_local_size = div+1;
1266
1267 // Create a NumericVector to hold the parallel solution
1268 std::unique_ptr<NumericVector<Number>> parallel_soln_ptr = NumericVector<Number>::build(_communicator);
1269 NumericVector<Number> & parallel_soln = *parallel_soln_ptr;
1270 parallel_soln.init(parallel_soln_global_size,
1271 parallel_soln_local_size,
1272 /*fast=*/false,
1273 /*ParallelType=*/PARALLEL);
1274
1275 unsigned int sys_ctr = 0;
1276 unsigned int var_ctr = 0;
1277 for (auto i : index_range(var_nums))
1278 {
1279 std::pair<unsigned int, unsigned int> var_num = var_nums[i];
1280 const System & system = this->get_system(var_num.first);
1281
1282 // Update the current_local_solution if necessary
1283 if (sys_ctr != var_num.first)
1284 {
1285 System & non_const_sys = const_cast<System &>(system);
1286 // We used to simply call non_const_sys.solution->close()
1287 // here, but that is not allowed when the solution vector is
1288 // locked read-only, for example when printing the solution
1289 // during during the middle of a solve... So try to be a bit
1290 // more careful about calling close() unnecessarily.
1291 libmesh_assert(this->comm().verify(non_const_sys.solution->closed()));
1292 if (!non_const_sys.solution->closed())
1293 non_const_sys.solution->close();
1294 non_const_sys.update();
1295 sys_ctr = var_num.first;
1296 }
1297
1299
1300 const unsigned int var = var_num.second;
1301
1302 const Variable & variable = system.variable(var);
1303 const DofMap & dof_map = system.get_dof_map();
1304
1305 // We need to check if the elemental data variable is a scalar or a vector and set the number of
1306 // components for the latter as per es.find_variable_numbers().
1307 // Even for the case where a variable is not active on any subdomain belonging to the
1308 // processor, we still need to know this number to update 'var_ctr'.
1309 const auto & var_type = system.variable_type(var);
1310 const unsigned int n_comps =
1311 (FEInterface::field_type(var_type) == TYPE_VECTOR) ?
1312 FEInterface::n_vec_dim(_mesh, var_type) : 1;
1313
1314 // Loop over all elements in the mesh and index all components of the variable if it's active
1317 [&dof_map, &variable, ne, var, var_ctr, n_comps,
1318 &parallel_soln, &sys_soln](const ConstElemRange & range)
1319 {
1320 // The DOF indices for the finite element
1321 std::vector<dof_id_type> dof_indices;
1322
1323 for (const Elem * elem : range)
1324 {
1325 if (variable.active_on_subdomain(elem->subdomain_id()))
1326 {
1327 dof_map.dof_indices(elem, dof_indices, var);
1328
1329 // The number of DOF components needs to be equal to the expected number so that we know
1330 // where to store data to correctly correspond to variable names.
1331 libmesh_assert_equal_to(dof_indices.size(), n_comps);
1332
1333 for (unsigned int comp = 0; comp < n_comps; comp++)
1334 parallel_soln.set(ne * (var_ctr + comp) + elem->id(), sys_soln(dof_indices[comp]));
1335 }
1336 }
1337 });
1338
1339 var_ctr += n_comps;
1340 } // end loop over var_nums
1341
1342 // NOTE: number of output names might not be equal to the number passed to this function. Any that
1343 // aren't elemental data variables have been filtered out (see
1344 // EquationSystems::find_variable_numbers).
1345 //
1346 // But, if everything is accounted for properly, then names.size() == var_ctr
1347 libmesh_assert_equal_to(names.size(), var_ctr);
1348
1349 parallel_soln.close();
1350 return parallel_soln_ptr;
1351}
1352
1353
1354
1355void
1356EquationSystems::build_discontinuous_solution_vector
1357(std::vector<Number> & soln,
1358 const std::set<std::string> * system_names,
1359 const std::vector<std::string> * var_names,
1360 bool vertices_only,
1361 bool add_sides) const
1362{
1363 LOG_SCOPE("build_discontinuous_solution_vector()", "EquationSystems");
1364
1365 libmesh_assert (this->n_systems());
1366
1367 const std::vector<std::string> component_suffix = {"_x", "_y", "_z"};
1368 const auto requested_components =
1369 [this, var_names, &component_suffix](const System & system,
1370 const unsigned int var)
1371 {
1372 std::vector<unsigned int> components;
1373
1374 const std::string & var_name = system.variable_name(var);
1375 const FEType & fe_type = system.variable_type(var);
1376 const unsigned int n_vec_dim = FEInterface::n_vec_dim(_mesh, fe_type);
1377
1378 if (FEInterface::field_type(fe_type) == TYPE_VECTOR)
1379 {
1380 libmesh_error_msg_if(n_vec_dim > component_suffix.size(),
1381 "Invalid dim in build_discontinuous_solution_vector");
1382
1383 const bool use_all_components =
1384 (var_names == nullptr) ||
1385 std::count(var_names->begin(), var_names->end(), var_name);
1386
1387 if (n_vec_dim <= 1)
1388 {
1389 if (use_all_components)
1390 components.push_back(0);
1391 }
1392 else
1393 for (auto comp : make_range(n_vec_dim))
1394 {
1395 const std::string component_name = var_name + component_suffix[comp];
1396 if (use_all_components ||
1397 std::count(var_names->begin(), var_names->end(), component_name))
1398 components.push_back(comp);
1399 }
1400 }
1401 else if (var_names == nullptr ||
1402 std::count(var_names->begin(), var_names->end(), var_name))
1403 components.push_back(0);
1404
1405 return components;
1406 };
1407
1408 // Get the number of variables (nv) by counting the number of variables
1409 // in each system listed in system_names
1410 unsigned int nv = 0;
1411
1412 for (const auto & [sys_name, sys_ptr] : _systems)
1413 {
1414 // Check current system is listed in system_names, and skip pos if not
1415 bool use_current_system = (system_names == nullptr);
1416 if (!use_current_system)
1417 use_current_system = system_names->count(sys_name);
1418 if (!use_current_system || sys_ptr->hide_output())
1419 continue;
1420
1421 // Loop over all variables in this System and check whether we
1422 // are supposed to use each one.
1423 for (auto var_id : make_range(sys_ptr->n_vars()))
1424 nv += cast_int<unsigned int>(requested_components(*sys_ptr, var_id).size());
1425 }
1426
1427 // get the total "weight" - the number of nodal values to write for
1428 // each variable.
1429 unsigned int tw=0;
1430 for (const auto & elem : _mesh.active_element_ptr_range())
1431 {
1432 tw += vertices_only ? elem->n_vertices() : elem->n_nodes();
1433
1434 if (add_sides)
1435 {
1436 for (auto s : elem->side_index_range())
1437 {
1438 if (redundant_added_side(*elem,s))
1439 continue;
1440
1441 const std::vector<unsigned int> side_nodes =
1442 elem->nodes_on_side(s);
1443
1444 if (!vertices_only)
1445 tw += side_nodes.size();
1446 else
1447 for (auto n : index_range(side_nodes))
1448 if (elem->is_vertex(side_nodes[n]))
1449 ++tw;
1450 }
1451 }
1452 }
1453
1454 // Only if we are on processor zero, allocate the storage
1455 // to hold (number_of_nodes)*(number_of_variables) entries.
1456 if (_mesh.processor_id() == 0)
1457 soln.resize(tw*nv);
1458
1459 std::vector<Number> sys_soln;
1460
1461 // Keep track of the variable "offset". This is used for indexing
1462 // into the global solution vector.
1463 unsigned int var_offset = 0;
1464
1465 // For each system in this EquationSystems object,
1466 // update the global solution and if we are on processor 0,
1467 // loop over the elements and build the nodal solution
1468 // from the element solution. Then insert this nodal solution
1469 // into the vector passed to build_solution_vector.
1470 for (const auto & [sys_name, system] : _systems)
1471 {
1472 // Check current system is listed in system_names, and skip pos if not
1473 bool use_current_system = (system_names == nullptr);
1474 if (!use_current_system)
1475 use_current_system = system_names->count(sys_name);
1476 if (!use_current_system || system->hide_output())
1477 continue;
1478
1479 const unsigned int nv_sys = system->n_vars();
1480 const auto & dof_map = system->get_dof_map();
1481
1482 system->update_global_solution (sys_soln, 0);
1483
1484 // Keep track of the number of vars actually written.
1485 unsigned int n_vars_written_current_system = 0;
1486
1487 if (_mesh.processor_id() == 0)
1488 {
1489 std::vector<Number> soln_coeffs; // The finite element solution coeffs
1490 std::vector<Number> nodal_soln; // The FE solution interpolated to the nodes
1491 std::vector<dof_id_type> dof_indices; // The DOF indices for the finite element
1492
1493 // For each variable, determine if we are supposed to
1494 // write it, then loop over the active elements, compute
1495 // the nodal_soln and store it to the "soln" vector. We
1496 // store zeros for subdomain-restricted variables on
1497 // elements where they are not active.
1498 for (auto var : make_range(nv_sys))
1499 {
1500 const std::vector<unsigned int> components_to_write =
1501 requested_components(*system, var);
1502
1503 // If we aren't supposed to write this var, go to the
1504 // next loop iteration.
1505 if (components_to_write.empty())
1506 continue;
1507
1508 const FEType & fe_type = system->variable_type(var);
1509 const Variable & var_description = system->variable(var);
1510 const bool add_p_level = fe_type.p_refinement;
1511 const unsigned int n_vec_dim = FEInterface::n_vec_dim(_mesh, fe_type);
1512
1513 unsigned int nn=0;
1514
1515 for (auto & elem : _mesh.active_element_ptr_range())
1516 {
1517 if (var_description.active_on_subdomain(elem->subdomain_id()))
1518 {
1519 dof_map.dof_indices (elem, dof_indices, var);
1520
1521 soln_coeffs.resize(dof_indices.size());
1522
1523 for (auto i : index_range(dof_indices))
1524 soln_coeffs[i] = sys_soln[dof_indices[i]];
1525
1526 // Compute the FE solution at all the nodes, but
1527 // only use the first n_vertices() entries if
1528 // vertices_only == true.
1529 FEInterface::nodal_soln (elem->dim(),
1530 fe_type,
1531 elem,
1532 soln_coeffs,
1533 nodal_soln,
1534 add_p_level,
1535 n_vec_dim);
1536
1537 // infinite elements should be skipped...
1538 if (!elem->infinite())
1539 {
1540 libmesh_assert_equal_to (nodal_soln.size(), elem->n_nodes()*n_vec_dim);
1541
1542 const unsigned int n_vals =
1543 vertices_only ? elem->n_vertices() : elem->n_nodes();
1544
1545 for (auto n : make_range(n_vals))
1546 {
1547 // Compute index into global solution vector.
1548 std::size_t index =
1549 nv * (nn++) + (n_vars_written_current_system + var_offset);
1550
1551 for (auto component_index : index_range(components_to_write))
1552 soln[index + component_index] +=
1553 nodal_soln[n_vec_dim*n + components_to_write[component_index]];
1554 }
1555 }
1556 }
1557 else
1558 nn += vertices_only ? elem->n_vertices() : elem->n_nodes();
1559 } // end loop over active elements writing interiors
1560
1561 // Loop writing "fake" sides, if requested
1562 if (add_sides)
1563 {
1564 // We don't build discontinuous solution vectors in
1565 // parallel yet, but we'll do ordering of fake side
1566 // values as if we did, for consistency with the
1567 // parallel continuous ordering and for future
1568 // compatibility.
1569 std::vector<std::vector<const Elem *>>
1570 elems_by_pid(_mesh.n_processors());
1571
1572 for (const auto & elem : _mesh.active_element_ptr_range())
1573 elems_by_pid[elem->processor_id()].push_back(elem);
1574
1575 for (auto p : index_range(elems_by_pid))
1576 for (const Elem * elem : elems_by_pid[p])
1577 {
1578 if (var_description.active_on_subdomain(elem->subdomain_id()))
1579 {
1580 dof_map.dof_indices (elem, dof_indices, var);
1581
1582 soln_coeffs.resize(dof_indices.size());
1583
1584 for (auto i : index_range(dof_indices))
1585 soln_coeffs[i] = sys_soln[dof_indices[i]];
1586
1587 for (auto s : elem->side_index_range())
1588 {
1589 if (redundant_added_side(*elem,s))
1590 continue;
1591
1592 const std::vector<unsigned int> side_nodes =
1593 elem->nodes_on_side(s);
1594
1595 // Compute the FE solution at all the
1596 // side nodes, but only use those for
1597 // which is_vertex() == true if
1598 // vertices_only == true.
1599 FEInterface::side_nodal_soln
1600 (fe_type, elem, s, soln_coeffs,
1601 nodal_soln, add_p_level,
1602 n_vec_dim);
1603
1604 libmesh_assert_equal_to
1605 (nodal_soln.size(),
1606 side_nodes.size()*n_vec_dim);
1607
1608 // If we don't have a continuous FE
1609 // then we want to average between
1610 // sides, at least in the equal-level
1611 // case where it's easy. This is
1612 // analogous to our repeat_count
1613 // behavior elsewhere.
1614 const FEContinuity cont =
1615 FEInterface::get_continuity(fe_type);
1616 const Elem * const neigh = elem->neighbor_ptr(s);
1617
1618 if ((cont == DISCONTINUOUS || cont == H_CURL || cont == H_DIV) &&
1619 neigh &&
1620 neigh->level() == elem->level() &&
1621 var_description.active_on_subdomain(neigh->subdomain_id()))
1622 {
1623 std::vector<dof_id_type> neigh_indices;
1624 dof_map.dof_indices (neigh, neigh_indices, var);
1625 std::vector<Number> neigh_coeffs(neigh_indices.size());
1626
1627 for (auto i : index_range(neigh_indices))
1628 neigh_coeffs[i] = sys_soln[neigh_indices[i]];
1629
1630 const unsigned int s_neigh =
1631 neigh->which_neighbor_am_i(elem);
1632 std::vector<Number> neigh_soln;
1633 FEInterface::side_nodal_soln
1634 (fe_type, neigh, s_neigh,
1635 neigh_coeffs, neigh_soln, add_p_level,
1636 n_vec_dim);
1637
1638 const std::vector<unsigned int> neigh_nodes =
1639 neigh->nodes_on_side(s_neigh);
1640 for (auto n : index_range(side_nodes))
1641 for (auto neigh_n : index_range(neigh_nodes))
1642 if (neigh->node_ptr(neigh_nodes[neigh_n])
1643 == elem->node_ptr(side_nodes[n]))
1644 for (auto comp : make_range(n_vec_dim))
1645 {
1646 const auto nodal_index = n_vec_dim*n + comp;
1647 nodal_soln[nodal_index] +=
1648 neigh_soln[n_vec_dim*neigh_n + comp];
1649 nodal_soln[nodal_index] /= 2;
1650 }
1651 }
1652
1653 for (auto n : index_range(side_nodes))
1654 {
1655 if (vertices_only &&
1656 !elem->is_vertex(n))
1657 continue;
1658
1659 // Compute index into global solution vector.
1660 std::size_t index =
1661 nv * (nn++) + (n_vars_written_current_system + var_offset);
1662
1663 for (auto component_index : index_range(components_to_write))
1664 soln[index + component_index] +=
1665 nodal_soln[n_vec_dim*n + components_to_write[component_index]];
1666 }
1667 }
1668 }
1669 else
1670 {
1671 nn += vertices_only ? elem->n_vertices() : elem->n_nodes();
1672
1673 for (auto s : elem->side_index_range())
1674 {
1675 if (redundant_added_side(*elem,s))
1676 continue;
1677
1678 const std::vector<unsigned int> side_nodes =
1679 elem->nodes_on_side(s);
1680
1681 for (auto n : index_range(side_nodes))
1682 {
1683 if (vertices_only &&
1684 !elem->is_vertex(n))
1685 continue;
1686 nn++;
1687 }
1688 }
1689 }
1690 } // end loop over active elements, writing "fake" sides
1691 }
1692 // If we made it here, we actually wrote a variable, so increment
1693 // the number of variables actually written for the current system.
1694 n_vars_written_current_system += cast_int<unsigned int>(components_to_write.size());
1695
1696 } // end loop over vars
1697 } // end if proc 0
1698
1699 // Update offset for next loop iteration.
1700 var_offset += n_vars_written_current_system;
1701 } // end loop over systems
1702}
1703
1704
1705
1706bool EquationSystems::redundant_added_side(const Elem & elem, unsigned int side)
1707{
1708 libmesh_assert(elem.active());
1709
1710 const Elem * neigh = elem.neighbor_ptr(side);
1711
1712 // Write boundary sides.
1713 if (!neigh)
1714 return false;
1715
1716 // Write ghost sides in Nemesis
1717 if (neigh == remote_elem)
1718 return false;
1719
1720 // Don't write a coarser side if a finer side exists
1721 if (!neigh->active())
1722 return true;
1723
1724 // Don't write a side redundantly from both of the
1725 // elements sharing it. We'll disambiguate with id().
1726 return (neigh->id() < elem.id());
1727}
1728
1729
1730
1731bool EquationSystems::compare (const EquationSystems & other_es,
1732 const Real threshold,
1733 const bool verbose) const
1734{
1735 // safety check, whether we handle at least the same number
1736 // of systems
1737 std::vector<bool> os_result;
1738
1739 if (this->n_systems() != other_es.n_systems())
1740 {
1741 if (verbose)
1742 {
1743 libMesh::out << " Fatal difference. This system handles "
1744 << this->n_systems() << " systems," << std::endl
1745 << " while the other system handles "
1746 << other_es.n_systems()
1747 << " systems." << std::endl
1748 << " Aborting comparison." << std::endl;
1749 }
1750 return false;
1751 }
1752 else
1753 {
1754 // start comparing each system
1755 for (const auto & [sys_name, sys_ptr] : _systems)
1756 {
1757 // get the other system
1758 const System & other_system = other_es.get_system (sys_name);
1759
1760 os_result.push_back (sys_ptr->compare (other_system, threshold, verbose));
1761
1762 }
1763
1764 }
1765
1766
1767 // sum up the results
1768 if (os_result.size()==0)
1769 return true;
1770 else
1771 {
1772 bool os_identical;
1773 unsigned int n = 0;
1774 do
1775 {
1776 os_identical = os_result[n];
1777 n++;
1778 }
1779 while (os_identical && n<os_result.size());
1780 return os_identical;
1781 }
1782}
1783
1784
1785
1786std::string EquationSystems::get_info () const
1787{
1788 std::ostringstream oss;
1789
1790 unsigned int n_hidden_sys = 0;
1791 for (auto & pr : _systems)
1792 n_hidden_sys += pr.second->hide_output();
1793
1794 oss << " EquationSystems\n"
1795 << " n_systems()=" << this->n_systems()
1796 << (n_hidden_sys ? " (hidden: " + std::to_string(n_hidden_sys) + ")" : "")
1797 << "\n";
1798
1799 // Print the info for the individual systems
1800 for (const auto & pr : _systems)
1801 if (!pr.second->hide_output())
1802 oss << pr.second->get_info();
1803
1804
1805 // // Possibly print the parameters
1806 // if (!this->parameters.empty())
1807 // {
1808 // oss << " n_parameters()=" << this->n_parameters() << '\n';
1809 // oss << " Parameters:\n";
1810
1811 // for (const auto & [key, val] : _parameters)
1812 // oss << " "
1813 // << "\""
1814 // << key
1815 // << "\""
1816 // << "="
1817 // << val
1818 // << '\n';
1819 // }
1820
1821 return oss.str();
1822}
1823
1824
1825
1826void EquationSystems::print_info (std::ostream & os) const
1827{
1828 os << this->get_info()
1829 << std::endl;
1830}
1831
1832
1833
1834std::ostream & operator << (std::ostream & os,
1835 const EquationSystems & es)
1836{
1837 es.print_info(os);
1838 return os;
1839}
1840
1841
1842
1843unsigned int EquationSystems::n_vars () const
1844{
1845 unsigned int tot=0;
1846
1847 for (const auto & pr : _systems)
1848 tot += pr.second->n_vars();
1849
1850 return tot;
1851}
1852
1853
1854
1855std::size_t EquationSystems::n_dofs () const
1856{
1857 std::size_t tot=0;
1858
1859 for (const auto & pr : _systems)
1860 tot += pr.second->n_dofs();
1861
1862 return tot;
1863}
1864
1865
1866
1867
1868std::size_t EquationSystems::n_active_dofs () const
1869{
1870 std::size_t tot=0;
1871
1872 for (const auto & pr : _systems)
1873 tot += pr.second->n_active_dofs();
1874
1875 return tot;
1876}
1877
1878
1879void EquationSystems::_add_system_to_nodes_and_elems()
1880{
1881 // All the nodes
1882 for (auto & node : _mesh.node_ptr_range())
1883 node->add_system();
1884
1885 // All the elements
1886 Threads::parallel_for
1887 (_mesh.element_stored_range(),
1888 [](const ElemRange & range)
1889 {
1890 for (Elem * elem : range)
1891 elem->add_system();
1892 });
1893}
1894
1895void EquationSystems::_remove_default_ghosting(unsigned int sys_num)
1896{
1897 this->get_system(sys_num).get_dof_map().remove_default_ghosting();
1898}
1899
1900} // namespace libMesh
unsigned int dim
processor_id_type size() const
processor_id_type rank() const
void allgather(const T &send_data, std::vector< T, A > &recv_data) const
This class handles the numbering of degrees of freedom on a mesh.
Definition dof_map.h:181
void remove_default_ghosting()
Remove any default ghosting functor(s).
Definition dof_map.C:1988
std::size_t distribute_dofs(MeshBase &)
Distribute dofs on the current mesh.
Definition dof_map.C:949
void dof_indices(const Elem *const elem, std::vector< dof_id_type > &di) const
Definition dof_map.C:2201
void add_default_ghosting()
Add the default functor(s) for coupling and algebraic ghosting.
Definition dof_map.C:1996
unsigned int n_dofs(const unsigned int s, const unsigned int var=libMesh::invalid_uint) const
Definition dof_object.h:797
dof_id_type id() const
Definition dof_object.h:819
This is the base class from which all geometric element types are derived.
Definition elem.h:96
bool active() const
Definition elem.h:2958
unsigned int which_neighbor_am_i(const Elem *e) const
This function tells you which neighbor e is.
Definition elem.h:2936
virtual std::vector< unsigned int > nodes_on_side(const unsigned int) const =0
subdomain_id_type subdomain_id() const
Definition elem.h:2591
unsigned int level() const
Definition elem.h:3091
const Node * node_ptr(const unsigned int i) const
Definition elem.h:2516
const Elem * neighbor_ptr(unsigned int i) const
Definition elem.h:2615
This is the EquationSystems class.
std::vector< std::pair< unsigned int, unsigned int > > find_elemental_data_variable_numbers(std::vector< std::string > &names) const
Finds system and variable numbers for variables that can be represented as elemental data.
virtual void reinit()
Handle any mesh changes and reinitialize all the systems on the updated mesh.
static bool is_elemental_data_fe_type(const FEType &type)
std::vector< std::pair< unsigned int, unsigned int > > find_variable_numbers_by_predicate(std::vector< std::string > &names, const std::function< bool(const FEType &)> &type_filter) const
Implementation detail for find_variable_numbers() variants.
std::unique_ptr< NumericVector< Number > > build_parallel_elemental_solution_vector(std::vector< std::string > &names) const
Builds a parallel vector of elemental data solution values corresponding to the entries in the input ...
EquationSystems(MeshBase &mesh)
Constructor.
void print_info(std::ostream &os=libMesh::out) const
Prints information about the equation systems, by default to libMesh::out.
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.
virtual ~EquationSystems()
Destructor.
virtual void sensitivity_solve(const ParameterVector &parameters)
Call sensitivity_solve on all the individual equation systems.
void update()
Updates local values for all the systems.
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 'type' or of 'types' corresponding to the entr...
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.
void build_elemental_solution_vector(std::vector< Number > &soln, std::vector< std::string > &names) const
Retrieve the solution data for elemental data variables.
unsigned int n_systems() const
static bool redundant_added_side(const Elem &elem, unsigned int side)
unsigned int n_vars() const
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.
const MeshBase & get_mesh() const
Parameters parameters
Data structure holding arbitrary parameters.
void build_elemental_data_variable_names(std::vector< std::string > &var_names, const std::set< std::string > *system_names=nullptr) const
Filter var_names to names of variables that can be represented as elemental data.
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.
bool _refine_in_reinit
Flag for whether to call coarsen/refine in reinit().
MeshBase & _mesh
The mesh data structure.
bool reinit_solutions()
Handle any mesh changes and project any solutions onto the updated mesh.
virtual void init()
Initialize all the systems.
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.
virtual void reinit_mesh()
Handle the association of a completely new mesh with the EquationSystem and all the Systems assigned ...
virtual void adjoint_solve(const QoISet &qoi_indices=QoISet())
Call adjoint_solve on all the individual equation systems.
virtual void reinit_systems()
Reinitialize all systems on the current mesh.
virtual void clear()
Restores the data structure to a pristine state.
const T_sys & get_system(std::string_view name) const
virtual void enable_default_ghosting(bool enable)
Enable or disable default ghosting functors on the Mesh and on all Systems.
virtual void solve()
Call solve on all the individual equation systems.
bool _enable_default_ghosting
Flag for whether to enable default ghosting on newly added Systems.
std::map< std::string, std::unique_ptr< System >, std::less<> > _systems
Data structure holding the systems.
void allgather()
Serializes a distributed mesh and its associated degree of freedom numbering for all systems.
static unsigned int n_vec_dim(const MeshBase &mesh, const FEType &fe_type)
static FEFieldType field_type(const FEType &fe_type)
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.
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.
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
Definition fe_type.h:197
OrderWrapper order
The approximation order of the element (at 0 p-refinement level).
Definition fe_type.h:203
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
FEFamily family
The type of finite element.
Definition fe_type.h:228
This is the MeshBase class.
Definition mesh_base.h:81
virtual bool is_serial() const
Definition mesh_base.h:357
virtual bool contract()=0
Delete subactive (i.e.
virtual dof_id_type n_elem() const =0
unsigned int spatial_dimension() const
Definition mesh_base.C:606
const ConstElemRange & active_local_element_stored_range() const
Definition mesh_base.C:1954
const ElemRange & element_stored_range()
Definition mesh_base.C:1939
virtual dof_id_type max_node_id() const =0
virtual dof_id_type max_elem_id() const =0
virtual void allgather()
Gathers all elements and nodes of the mesh onto every processor.
Definition mesh_base.h:386
Implements (adaptive) mesh refinement algorithms for a MeshBase.
bool coarsen_elements()
Only coarsens the user-requested elements.
void clean_refinement_flags()
Sets the refinement flag to Elem::DO_NOTHING for each element in the mesh.
unsigned char & face_level_mismatch_limit()
If face_level_mismatch_limit is set to a nonzero value, then refinement and coarsening will produce m...
signed char & underrefined_boundary_limit()
If underrefined_boundary_limit is set to a nonnegative value, then refinement and coarsening will pro...
bool refine_elements()
Only refines the user-requested elements.
signed char & overrefined_boundary_limit()
If overrefined_boundary_limit is set to a nonnegative value, then refinement and coarsening will prod...
A Node is like a Point, but with more information.
Definition node.h:55
Provides a uniform interface to vector storage schemes for different linear algebra libraries.
virtual void set(const numeric_index_type i, const T value)=0
Sets v(i) = value.
virtual void get(const std::vector< numeric_index_type > &index, T *values) const
Access multiple components at once.
virtual numeric_index_type last_local_index() const =0
virtual void close()=0
Calls the NumericVector's internal assembly routines, ensuring that the values are consistent across ...
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...
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.
virtual void add(const numeric_index_type i, const T value)=0
Adds value to the vector entry specified by i.
virtual numeric_index_type first_local_index() const =0
An object whose state is distributed along a set of processors.
const Parallel::Communicator & _communicator
processor_id_type processor_id() const
const Parallel::Communicator & comm() const
processor_id_type n_processors() const
Data structure for specifying which Parameters should be independent variables in a parameter sensiti...
virtual void clear()
Clears internal data structures & frees any allocated memory.
Definition parameters.h:349
T & set(const std::string &)
Definition parameters.h:494
Data structure for specifying which Quantities of Interest should be calculated in an adjoint or a pa...
Definition qoi_set.h:46
The StoredRange class defines a contiguous, divisible set of objects.
Manages consistently variables, degrees of freedom, and coefficient vectors.
Definition system.h:100
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
const Variable & variable(unsigned int var) const
Return a constant reference to Variable var.
Definition system.C:2704
const FEType & variable_type(const unsigned int i) const
Definition system.C:2721
virtual void prolong_vectors()
Prolong vectors after the mesh has refined.
Definition system.C:432
virtual void restrict_vectors()
Restrict vectors after the mesh has coarsened.
Definition system.C:374
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
Definition system.h:1655
const std::string & variable_name(const unsigned int i) const
Definition system.C:2679
virtual void update()
Update the local values to reflect the solution on neighboring processors.
Definition system.C:498
virtual void reinit_constraints()
Reinitializes the constraints for this system.
Definition system.C:483
unsigned int n_vars() const
Definition system.C:2674
const DofMap & get_dof_map() const
Definition system.h:2417
unsigned int number() const
Definition system.h:2393
This class defines the notion of a variable in the system.
Definition variable.h:51
const std::set< subdomain_id_type > & active_subdomains() const
Definition variable.h:181
bool active_on_subdomain(subdomain_id_type sid) const
Definition variable.h:167
MeshBase & mesh
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.
The libMesh namespace provides an interface to certain functionality in the library.
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
libmesh_assert(ctx)
OStreamProxy out
dof_id_type numeric_index_type
Definition id_types.h:99
const RemoteElem * remote_elem
Definition remote_elem.C:57
static constexpr Real TOLERANCE
uint8_t dof_id_type
Definition id_types.h:67
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
uint8_t processor_id_type
Definition id_types.h:104
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