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