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