libMesh
Loading...
Searching...
No Matches
rb_construction.C
Go to the documentation of this file.
1// rbOOmit: An implementation of the Certified Reduced Basis method.
2// Copyright (C) 2009, 2010 David J. Knezevic
3
4// This file is part of rbOOmit.
5
6// rbOOmit is free software; you can redistribute it and/or
7// modify it under the terms of the GNU Lesser General Public
8// License as published by the Free Software Foundation; either
9// version 2.1 of the License, or (at your option) any later version.
10
11// rbOOmit is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14// Lesser General Public License for more details.
15
16// You should have received a copy of the GNU Lesser General Public
17// License along with this library; if not, write to the Free Software
18// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20// rbOOmit includes
21#include "libmesh/rb_construction_base.h"
22#include "libmesh/rb_construction.h"
23#include "libmesh/rb_assembly_expansion.h"
24#include "libmesh/rb_evaluation.h"
25#include "libmesh/elem_assembly.h"
26
27// LibMesh includes
28#include "libmesh/numeric_vector.h"
29#include "libmesh/sparse_matrix.h"
30#include "libmesh/dof_map.h"
31#include "libmesh/libmesh_logging.h"
32#include "libmesh/equation_systems.h"
33#include "libmesh/exodusII_io.h"
34#include "libmesh/gmv_io.h"
35#include "libmesh/linear_solver.h"
36#include "libmesh/getpot.h"
37#include "libmesh/int_range.h"
38#include "libmesh/mesh_base.h"
39#include "libmesh/parallel.h"
40#include "libmesh/xdr_cxx.h"
41#include "libmesh/timestamp.h"
42#include "libmesh/petsc_linear_solver.h"
43#include "libmesh/dg_fem_context.h"
44#include "libmesh/dirichlet_boundaries.h"
45#include "libmesh/zero_function.h"
46#include "libmesh/coupling_matrix.h"
47#include "libmesh/face_tri3_subdivision.h"
48#include "libmesh/quadrature.h"
49#include "libmesh/utility.h"
50
51// C++ includes
52#include <sys/types.h>
53#include <sys/stat.h>
54#include <errno.h>
55#include <fstream>
56#include <sstream>
57#include <limits>
58#include <stdlib.h> // mkstemps on Linux
59#ifdef LIBMESH_HAVE_UNISTD_H
60#include <unistd.h> // mkstemps on MacOS
61#endif
62
63namespace libMesh
64{
65
67 const std::string & name_in,
68 const unsigned int number_in)
69 : Parent(es, name_in, number_in),
70 inner_product_solver(LinearSolver<Number>::build(es.comm())),
71 extra_linear_solver(nullptr),
72 inner_product_matrix(SparseMatrix<Number>::build(es.comm())),
73 skip_residual_in_train_reduced_basis(false),
74 exit_on_repeated_greedy_parameters(true),
75 impose_internal_fluxes(false),
76 skip_degenerate_sides(true),
77 compute_RB_inner_product(false),
78 store_dirichlet_operators(true),
79 store_non_dirichlet_operators(false),
80 store_untransformed_basis(false),
81 use_empty_rb_solve_in_greedy(true),
82 Fq_representor_innerprods_computed(false),
83 Nmax(0),
84 delta_N(1),
85 output_dual_innerprods_computed(false),
86 assert_convergence(true),
87 rb_eval(nullptr),
88 inner_product_assembly(nullptr),
89 use_energy_inner_product(false),
90 rel_training_tolerance(1.e-4),
91 abs_training_tolerance(1.e-12),
92 normalize_rb_bound_in_greedy(false),
93 RB_training_type("Greedy"),
94 _preevaluate_thetas_flag(false),
95 _preevaluate_thetas_completed(false)
96{
97 // set assemble_before_solve flag to false
98 // so that we control matrix assembly.
100}
101
103
105{
106 LOG_SCOPE("clear()", "RBConstruction");
107
109
111 {
112 Aq_vector.clear();
113 Fq_vector.clear();
114 outputs_vector.clear();
115 }
116
118 {
122 }
123
124 // Also delete the Fq representors
125 Fq_representor.clear();
126
127 // Set Fq_representor_innerprods_computed flag to false now
128 // that we've cleared the Fq representors
130}
131
132std::string RBConstruction::system_type () const
133{
134 return "RBConstruction";
135}
136
138 SparseMatrix<Number> & input_matrix,
139 NumericVector<Number> & input_rhs)
140{
141 // This is similar to LinearImplicitSysmte::solve()
142
143 // Get a reference to the EquationSystems
144 const EquationSystems & es =
145 this->get_equation_systems();
146
147 // If the linear solver hasn't been initialized, we do so here.
148 input_solver.init();
149
150 // Get the user-specifiied linear solver tolerance
151 const double tol =
152 double(es.parameters.get<Real>("linear solver tolerance"));
153
154 // Get the user-specified maximum # of linear solver iterations
155 const unsigned int maxits =
156 es.parameters.get<unsigned int>("linear solver maximum iterations");
157
158 // It's good practice to clear the solution vector first since it can
159 // affect convergence of iterative solvers
160 solution->zero();
161
162 // Solve the linear system.
163 // Store the number of linear iterations required to
164 // solve and the final residual.
166 input_solver.solve (input_matrix, *solution, input_rhs, tol, maxits);
167
169
170 // Update the system after the solve
171 this->update();
172}
173
175{
176 rb_eval = &rb_eval_in;
177}
178
180{
181 libmesh_error_msg_if(!rb_eval, "Error: RBEvaluation object hasn't been initialized yet");
182
183 return *rb_eval;
184}
185
187{
188 libmesh_error_msg_if(!rb_eval, "Error: RBEvaluation object hasn't been initialized yet");
189
190 return *rb_eval;
191}
192
194{
195 return (rb_eval != nullptr);
196}
197
202
207
208void RBConstruction::process_parameters_file (const std::string & parameters_filename)
209{
210 // First read in data from input_filename
211 GetPot infile(parameters_filename);
212
213 const unsigned int n_training_samples = infile("n_training_samples",0);
214 const bool deterministic_training = infile("deterministic_training",false);
215 unsigned int training_parameters_random_seed_in =
216 static_cast<unsigned int>(-1);
217 training_parameters_random_seed_in = infile("training_parameters_random_seed",
218 training_parameters_random_seed_in);
219 const bool quiet_mode_in = infile("quiet_mode", quiet_mode);
220 const unsigned int Nmax_in = infile("Nmax", Nmax);
221 const Real rel_training_tolerance_in = infile("rel_training_tolerance",
223 const Real abs_training_tolerance_in = infile("abs_training_tolerance",
225
226 // Initialize value to false, let the input file value override.
227 const bool normalize_rb_bound_in_greedy_in = infile("normalize_rb_bound_in_greedy",
228 false);
229
230 const std::string RB_training_type_in = infile("RB_training_type", "Greedy");
231
232 // Read in the parameters from the input file too
233 unsigned int n_continuous_parameters = infile.vector_variable_size("parameter_names");
234 RBParameters mu_min_in;
235 RBParameters mu_max_in;
236 for (unsigned int i=0; i<n_continuous_parameters; i++)
237 {
238 // Read in the parameter names
239 std::string param_name = infile("parameter_names", "NONE", i);
240
241 {
242 Real min_val = infile(param_name, 0., 0);
243 mu_min_in.set_value(param_name, min_val);
244 }
245
246 {
247 Real max_val = infile(param_name, 0., 1);
248 mu_max_in.set_value(param_name, max_val);
249 }
250 }
251
252 std::map<std::string, std::vector<Real>> discrete_parameter_values_in;
253
254 unsigned int n_discrete_parameters = infile.vector_variable_size("discrete_parameter_names");
255 for (unsigned int i=0; i<n_discrete_parameters; i++)
256 {
257 std::string param_name = infile("discrete_parameter_names", "NONE", i);
258
259 unsigned int n_vals_for_param = infile.vector_variable_size(param_name);
260 std::vector<Real> vals_for_param(n_vals_for_param);
261 for (auto j : make_range(vals_for_param.size()))
262 vals_for_param[j] = infile(param_name, 0., j);
263
264 discrete_parameter_values_in[param_name] = vals_for_param;
265 }
266
267 std::map<std::string,bool> log_scaling_in;
268 // For now, just set all entries to false.
269 // TODO: Implement a decent way to specify log-scaling true/false
270 // in the input text file
271 for (const auto & pr : mu_min_in)
272 log_scaling_in[pr.first] = false;
273
274 // Set the parameters that have been read in
275 set_rb_construction_parameters(n_training_samples,
276 deterministic_training,
277 static_cast<int>(training_parameters_random_seed_in),
278 quiet_mode_in,
279 Nmax_in,
280 rel_training_tolerance_in,
281 abs_training_tolerance_in,
282 normalize_rb_bound_in_greedy_in,
283 RB_training_type_in,
284 mu_min_in,
285 mu_max_in,
286 discrete_parameter_values_in,
287 log_scaling_in);
288}
289
291 unsigned int n_training_samples_in,
292 bool deterministic_training_in,
293 int training_parameters_random_seed_in,
294 bool quiet_mode_in,
295 unsigned int Nmax_in,
296 Real rel_training_tolerance_in,
297 Real abs_training_tolerance_in,
298 bool normalize_rb_bound_in_greedy_in,
299 const std::string & RB_training_type_in,
300 const RBParameters & mu_min_in,
301 const RBParameters & mu_max_in,
302 const std::map<std::string, std::vector<Real>> & discrete_parameter_values_in,
303 const std::map<std::string,bool> & log_scaling_in,
304 std::map<std::string, std::vector<RBParameter>> * training_sample_list)
305{
306 // Read in training_parameters_random_seed value. This is used to
307 // seed the RNG when picking the training parameters. By default the
308 // value is -1, which means use std::time to seed the RNG.
309 set_training_random_seed(training_parameters_random_seed_in);
310
311 // Set quiet mode
312 set_quiet_mode(quiet_mode_in);
313
314 // Initialize RB parameters
315 set_Nmax(Nmax_in);
316
317 set_rel_training_tolerance(rel_training_tolerance_in);
318 set_abs_training_tolerance(abs_training_tolerance_in);
319
320 set_normalize_rb_bound_in_greedy(normalize_rb_bound_in_greedy_in);
321
322 set_RB_training_type(RB_training_type_in);
323
324 // Initialize the parameter ranges and the parameters themselves
325 initialize_parameters(mu_min_in, mu_max_in, discrete_parameter_values_in);
326
327 bool updated_deterministic_training = deterministic_training_in;
328 if (training_sample_list && (this->get_parameters_min().n_parameters() > 3))
329 {
330 // In this case we force deterministic_training to be false because
331 // a) deterministic training samples are not currrently supported with
332 // more than 3 parameters, and
333 // b) we will overwrite the training samples anyway in the call to
334 // load_training_set() below, so we do not want to generate an
335 // error due to deterministic training sample generation when
336 // the samples will be overwritten anyway.
337 updated_deterministic_training = false;
338 }
339
341 this->get_parameters_max(),
342 n_training_samples_in,
343 log_scaling_in,
344 updated_deterministic_training); // use deterministic parameters
345
346 if (training_sample_list)
347 {
348 // Note that we must call initialize_training_parameters() before
349 // load_training_set() in order to initialize the parameter vectors.
350 load_training_set(*training_sample_list);
351 }
352}
353
355{
356 // Print out info that describes the current setup
357 libMesh::out << std::endl << "RBConstruction parameters:" << std::endl;
358 libMesh::out << "system name: " << this->name() << std::endl;
359 libMesh::out << "Nmax: " << Nmax << std::endl;
360 libMesh::out << "Greedy relative error tolerance: " << get_rel_training_tolerance() << std::endl;
361 libMesh::out << "Greedy absolute error tolerance: " << get_abs_training_tolerance() << std::endl;
362 libMesh::out << "Do we normalize RB error bound in greedy? " << get_normalize_rb_bound_in_greedy() << std::endl;
363 libMesh::out << "RB training type: " << get_RB_training_type() << std::endl;
365 {
366 libMesh::out << "Aq operators attached: " << get_rb_theta_expansion().get_n_A_terms() << std::endl;
367 libMesh::out << "Fq functions attached: " << get_rb_theta_expansion().get_n_F_terms() << std::endl;
368 libMesh::out << "n_outputs: " << get_rb_theta_expansion().get_n_outputs() << std::endl;
369 for (unsigned int n=0; n<get_rb_theta_expansion().get_n_outputs(); n++)
370 libMesh::out << "output " << n << ", Q_l = " << get_rb_theta_expansion().get_n_output_terms(n) << std::endl;
371 }
372 else
373 {
374 libMesh::out << "RBThetaExpansion member is not set yet" << std::endl;
375 }
376 libMesh::out << "Number of parameters: " << get_n_params() << std::endl;
377 for (const auto & pr : get_parameters())
378 if (!is_discrete_parameter(pr.first))
379 {
380 libMesh::out << "Parameter " << pr.first
381 << ": Min = " << get_parameter_min(pr.first)
382 << ", Max = " << get_parameter_max(pr.first) << std::endl;
383 }
384
386 libMesh::out << "n_training_samples: " << get_n_training_samples() << std::endl;
387 libMesh::out << "quiet mode? " << is_quiet() << std::endl;
388 libMesh::out << std::endl;
389}
390
392{
393 std::unique_ptr<NumericVector<Number>> temp = solution->clone();
394
395 for (unsigned int i=0; i<get_rb_evaluation().get_n_basis_functions(); i++)
396 {
397 for (unsigned int j=0; j<get_rb_evaluation().get_n_basis_functions(); j++)
398 {
400 Number value = temp->dot( get_rb_evaluation().get_basis_function(i) );
401
402 libMesh::out << value << " ";
403 }
404 libMesh::out << std::endl;
405 }
406 libMesh::out << std::endl;
407}
408
410{
411 rb_assembly_expansion = &rb_assembly_expansion_in;
412}
413
415{
416 libmesh_error_msg_if(!rb_assembly_expansion, "Error: RBAssemblyExpansion object hasn't been initialized yet");
417
418 return *rb_assembly_expansion;
419}
420
422{
424 inner_product_assembly = &inner_product_assembly_in;
425}
426
428{
429 libmesh_error_msg_if(use_energy_inner_product,
430 "Error: inner_product_assembly not available since we're using energy inner-product");
431
432 libmesh_error_msg_if(!inner_product_assembly,
433 "Error: inner_product_assembly hasn't been initialized yet");
434
436}
437
438void RBConstruction::set_energy_inner_product(const std::vector<Number> & energy_inner_product_coeffs_in)
439{
441 energy_inner_product_coeffs = energy_inner_product_coeffs_in;
442}
443
445{
446#ifdef LIBMESH_ENABLE_CONSTRAINTS
447 const DofMap & dof_map = get_dof_map();
448
449 for (dof_id_type i=dof_map.first_dof(); i<dof_map.end_dof(); i++)
450 {
452 {
453 vector.set(i, 0.);
454 }
455 }
456#endif
457
458 vector.close();
459}
460
462{
463 return (solution->l2_norm() == 0.);
464}
465
466void RBConstruction::initialize_rb_construction(bool skip_matrix_assembly,
467 bool skip_vector_assembly)
468{
469 if (!skip_matrix_assembly && !skip_vector_assembly)
470 {
471 // Check that the theta and assembly objects are consistently sized
472 libmesh_assert_equal_to (get_rb_theta_expansion().get_n_A_terms(), get_rb_assembly_expansion().get_n_A_terms());
473 libmesh_assert_equal_to (get_rb_theta_expansion().get_n_F_terms(), get_rb_assembly_expansion().get_n_F_terms());
474 libmesh_assert_equal_to (get_rb_theta_expansion().get_n_outputs(), get_rb_assembly_expansion().get_n_outputs());
475 for (unsigned int i=0; i<get_rb_theta_expansion().get_n_outputs(); i++)
476 {
477 libmesh_assert_equal_to (get_rb_theta_expansion().get_n_output_terms(i),
478 get_rb_assembly_expansion().get_n_output_terms(i));
479 }
480 }
481
482 // Perform the initialization
484 assemble_affine_expansion(skip_matrix_assembly, skip_vector_assembly);
485
486 // inner_product_solver performs solves with the same matrix every time
487 // hence we can set reuse_preconditioner(true).
488 inner_product_solver->reuse_preconditioner(true);
489
490 // The primary solver is used for truth solves and other solves that
491 // require different matrices, so set reuse_preconditioner(false).
493
494}
495
496void RBConstruction::assemble_affine_expansion(bool skip_matrix_assembly,
497 bool skip_vector_assembly)
498{
499 if (!skip_matrix_assembly)
500 {
501 // Assemble and store all of the matrices
504 }
505
506 if (!skip_vector_assembly)
507 {
508 // Assemble and store all of the vectors
511 }
512}
513
515{
516 // Resize vectors for storing mesh-dependent data
517 Aq_vector.resize(get_rb_theta_expansion().get_n_A_terms());
518 Fq_vector.resize(get_rb_theta_expansion().get_n_F_terms());
519
520 // Resize the Fq_representors and initialize each to nullptr.
521 // These are basis independent and hence stored here, whereas
522 // the Aq_representors are stored in RBEvaluation
523 Fq_representor.resize(get_rb_theta_expansion().get_n_F_terms());
524
525 // Initialize vectors for the inner products of the Fq representors
526 // These are basis independent and therefore stored here.
528 Fq_representor_innerprods.resize(Q_f_hat);
529
530 // Resize the output vectors
532 for (unsigned int n=0; n<get_rb_theta_expansion().get_n_outputs(); n++)
534
535 // Resize the output dual norm vectors
537 for (unsigned int n=0; n<get_rb_theta_expansion().get_n_outputs(); n++)
538 {
540 output_dual_innerprods[n].resize(Q_l_hat);
541 }
542
543 {
544 DofMap & dof_map = this->get_dof_map();
545
547 {
549 inner_product_matrix->init();
550 inner_product_matrix->zero();
551 }
552
554 {
555 // We also need a non-Dirichlet inner-product matrix
560 }
561
563 for (unsigned int q=0; q<get_rb_theta_expansion().get_n_A_terms(); q++)
564 {
565 // Initialize the memory for the matrices
567 dof_map.attach_matrix(*Aq_vector[q]);
568 Aq_vector[q]->init();
569 Aq_vector[q]->zero();
570 }
571
572 // We also need to initialize a second set of non-Dirichlet operators
574 {
575 non_dirichlet_Aq_vector.resize(get_rb_theta_expansion().get_n_A_terms());
576 for (unsigned int q=0; q<get_rb_theta_expansion().get_n_A_terms(); q++)
577 {
578 // Initialize the memory for the matrices
581 non_dirichlet_Aq_vector[q]->init();
582 non_dirichlet_Aq_vector[q]->zero();
583 }
584 }
585 }
586
587 // Initialize the vectors
589 for (unsigned int q=0; q<get_rb_theta_expansion().get_n_F_terms(); q++)
590 {
591 // Initialize the memory for the vectors
593 Fq_vector[q]->init (this->n_dofs(), this->n_local_dofs(), false, PARALLEL);
594 }
595
596 // We also need to initialize a second set of non-Dirichlet operators
598 {
599 non_dirichlet_Fq_vector.resize(get_rb_theta_expansion().get_n_F_terms());
600 for (unsigned int q=0; q<get_rb_theta_expansion().get_n_F_terms(); q++)
601 {
602 // Initialize the memory for the vectors
604 non_dirichlet_Fq_vector[q]->init (this->n_dofs(), this->n_local_dofs(), false, PARALLEL);
605 }
606 }
607
609 for (unsigned int n=0; n<get_rb_theta_expansion().get_n_outputs(); n++)
610 for (unsigned int q_l=0; q_l<get_rb_theta_expansion().get_n_output_terms(n); q_l++)
611 {
612 // Initialize the memory for the truth output vectors
614 outputs_vector[n][q_l]->init (this->n_dofs(), this->n_local_dofs(), false, PARALLEL);
615 }
616
618 {
620 for (unsigned int n=0; n<get_rb_theta_expansion().get_n_outputs(); n++)
621 {
623 for (unsigned int q_l=0; q_l<get_rb_theta_expansion().get_n_output_terms(n); q_l++)
624 {
625 // Initialize the memory for the truth output vectors
627 non_dirichlet_outputs_vector[n][q_l]->init (this->n_dofs(), this->n_local_dofs(), false, PARALLEL);
628 }
629 }
630 }
631
632 // Resize truth_outputs vector
633 truth_outputs.resize(this->get_rb_theta_expansion().get_n_outputs());
634}
635
636std::unique_ptr<DGFEMContext> RBConstruction::build_context ()
637{
638 return std::make_unique<DGFEMContext>(*this);
639}
640
642 ElemAssembly * elem_assembly,
643 SparseMatrix<Number> * input_matrix,
644 NumericVector<Number> * input_vector,
645 bool symmetrize,
646 bool apply_dof_constraints)
647{
648 LOG_SCOPE("add_scaled_matrix_and_vector()", "RBConstruction");
649
650 bool assemble_matrix = (input_matrix != nullptr);
651 bool assemble_vector = (input_vector != nullptr);
652
653 if (!assemble_matrix && !assemble_vector)
654 return;
655
656 const MeshBase & mesh = this->get_mesh();
657
658 // First add any node-based terms (e.g. point loads)
659
660 // Make a std::set of all the nodes that are in 1 or more
661 // nodesets. We only want to call get_nodal_values() once per Node
662 // per ElemAssembly object, regardless of how many nodesets it
663 // appears in.
664 std::set<dof_id_type> nodes_with_nodesets;
665 for (const auto & t : mesh.get_boundary_info().build_node_list())
666 nodes_with_nodesets.insert(std::get<0>(t));
667
668 // It's possible for the node assembly loop below to throw an
669 // exception on one (or some subset) of the processors. In that
670 // case, we stop assembling on the processor(s) that threw and let
671 // the other processors finish assembly. Then we synchronize to
672 // check whether an exception was thrown on _any_ processor, and if
673 // so, re-throw it on _all_ processors.
674 int nodal_assembly_threw = 0;
675
676 libmesh_try
677 {
678
679 for (const auto & id : nodes_with_nodesets)
680 {
681 const Node & node = mesh.node_ref(id);
682
683 // If node is on this processor, then all dofs on node are too
684 // so we can do the add below safely
685 if (node.processor_id() == this->comm().rank())
686 {
687 // Get the values to add to the rhs vector
688 std::vector<dof_id_type> nodal_dof_indices;
689 DenseMatrix<Number> nodal_matrix;
690 DenseVector<Number> nodal_rhs;
691 elem_assembly->get_nodal_values(nodal_dof_indices,
692 nodal_matrix,
693 nodal_rhs,
694 *this,
695 node);
696
697 // Perform any required user-defined postprocessing on
698 // the matrix and rhs.
699 //
700 // TODO: We need to postprocess node matrices and vectors
701 // in some cases (e.g. when rotations are applied to
702 // nodes), but since we don't have a FEMContext at this
703 // point we would need to have a different interface
704 // taking the DenseMatrix, DenseVector, and probably the
705 // current node that we are on...
706 // this->post_process_elem_matrix_and_vector(nodal_matrix, nodal_rhs);
707
708 if (!nodal_dof_indices.empty())
709 {
710 if (apply_dof_constraints)
711 {
712 // Apply constraints, e.g. Dirichlet and periodic constraints
714 nodal_matrix,
715 nodal_rhs,
716 nodal_dof_indices,
717 /*asymmetric_constraint_rows*/ false);
718 }
719
720 // Scale and add to global matrix and/or vector
721 nodal_matrix *= scalar;
722 nodal_rhs *= scalar;
723
724 if (assemble_vector)
725 input_vector->add_vector(nodal_rhs, nodal_dof_indices);
726
727 if (assemble_matrix)
728 input_matrix->add_matrix(nodal_matrix, nodal_dof_indices);
729 }
730 }
731 }
732 } // libmesh_try
733 libmesh_catch(...)
734 {
735 nodal_assembly_threw = 1;
736 }
737
738 // Check for exceptions on any procs and if there is one, re-throw
739 // it on all procs.
740 this->comm().max(nodal_assembly_threw);
741
742 if (nodal_assembly_threw)
743 libmesh_error_msg("Error during assembly in RBConstruction::add_scaled_matrix_and_vector()");
744
745 std::unique_ptr<DGFEMContext> c = this->build_context();
746 DGFEMContext & context = cast_ref<DGFEMContext &>(*c);
747
748 this->init_context(context);
749
750 // It's possible for the assembly loop below to throw an exception
751 // on one (or some subset) of the processors. This can happen when
752 // e.g. the mesh contains one or more elements with negative
753 // Jacobian. In that case, we stop assembling on the processor(s)
754 // that threw and let the other processors finish assembly. Then we
755 // synchronize to check whether an exception was thrown on _any_
756 // processor, and if so, re-throw it on _all_ processors. This way,
757 // we make it easier for callers to handle exceptions in parallel
758 // during assembly: they can simply assume that the code either
759 // throws on all procs or on no procs.
760 int assembly_threw = 0;
761
762 libmesh_try
763 {
764
765 for (const auto & elem : mesh.active_local_element_ptr_range())
766 {
767 const ElemType elemtype = elem->type();
768
769 if(elemtype == NODEELEM)
770 {
771 // We assume that we do not perform any assembly directly on
772 // NodeElems, so we skip the assembly calls.
773
774 // However, in a spline basis with Dirichlet constraints on
775 // spline nodes, a constrained matrix has to take those
776 // nodes into account.
777 if (!apply_dof_constraints)
778 continue;
779 }
780
781 // Subdivision elements need special care:
782 // - skip ghost elements
783 // - init special quadrature rule
784 std::unique_ptr<QBase> qrule;
785 if (elemtype == TRI3SUBDIVISION)
786 {
787 const Tri3Subdivision * gh_elem = static_cast<const Tri3Subdivision *> (elem);
788 if (gh_elem->is_ghost())
789 continue ;
790 // A Gauss quadrature rule for numerical integration.
791 // For subdivision shell elements, a single Gauss point per
792 // element is sufficient, hence we use extraorder = 0.
793 const int extraorder = 0;
794 FEBase * elem_fe = nullptr;
795 context.get_element_fe( 0, elem_fe );
796
797 qrule = elem_fe->get_fe_type().default_quadrature_rule (2, extraorder);
798
799 // Tell the finite element object to use our quadrature rule.
800 elem_fe->attach_quadrature_rule (qrule.get());
801 }
802
803 context.pre_fe_reinit(*this, elem);
804
805 // Do nothing in case there are no dof_indices on the current element
806 if ( context.get_dof_indices().empty() )
807 continue;
808
809 context.elem_fe_reinit();
810
811 if (elemtype != NODEELEM)
812 {
813 elem_assembly->interior_assembly(context);
814
815 const unsigned char n_sides = context.get_elem().n_sides();
816 for (context.side = 0; context.side != n_sides; ++context.side)
817 {
818 // May not need to apply fluxes on non-boundary elements
819 if ((context.get_elem().neighbor_ptr(context.get_side()) != nullptr) && !impose_internal_fluxes)
820 continue;
821
822 // skip degenerate sides with zero area
823 if( (context.get_elem().side_ptr(context.get_side())->volume() <= 0.) && skip_degenerate_sides)
824 continue;
825
826 context.side_fe_reinit();
827 elem_assembly->boundary_assembly(context);
828
829 if (context.dg_terms_are_active())
830 {
831 input_matrix->add_matrix (context.get_elem_elem_jacobian(),
832 context.get_dof_indices(),
833 context.get_dof_indices());
834
835 input_matrix->add_matrix (context.get_elem_neighbor_jacobian(),
836 context.get_dof_indices(),
837 context.get_neighbor_dof_indices());
838
839 input_matrix->add_matrix (context.get_neighbor_elem_jacobian(),
840 context.get_neighbor_dof_indices(),
841 context.get_dof_indices());
842
843 input_matrix->add_matrix (context.get_neighbor_neighbor_jacobian(),
844 context.get_neighbor_dof_indices(),
845 context.get_neighbor_dof_indices());
846 }
847 }
848 }
849
850 // Do any required user post-processing before symmetrizing and/or applying
851 // constraints.
852 //
853 // We only do this if apply_dof_constraints is true because we want to be
854 // able to set apply_dof_constraints=false in order to obtain a matrix
855 // A with no dof constraints or dof transformations, as opposed to C^T A C,
856 // which includes constraints and/or dof transformations. Here C refers to
857 // the matrix that imposes dof constraints and transformations on the
858 // solution u.
859 //
860 // Matrices such as A are what we store in our "non_dirichlet" operators, and
861 // they are useful for computing terms such as (C u_i)^T A (C u_j) (e.g. see
862 // update_RB_system_matrices()), where C u is the result of a "truth_solve",
863 // which includes calls to both enforce_constraints_exactly() and
864 // post_process_truth_solution(). If we use C^T A C to compute these terms then
865 // we would "double apply" the matrix C, which can give incorrect results.
866 if (apply_dof_constraints)
868
869 // Need to symmetrize before imposing
870 // periodic constraints
871 if (assemble_matrix && symmetrize)
872 {
873 DenseMatrix<Number> Ke_transpose;
874 context.get_elem_jacobian().get_transpose(Ke_transpose);
875 context.get_elem_jacobian() += Ke_transpose;
876 context.get_elem_jacobian() *= 0.5;
877 }
878
879 // As discussed above, we can set apply_dof_constraints=false to
880 // get A instead of C^T A C
881 if (apply_dof_constraints)
882 {
883 // Apply constraints, e.g. Dirichlet and periodic constraints
885 (context.get_elem_jacobian(),
886 context.get_elem_residual(),
887 context.get_dof_indices(),
888 /*asymmetric_constraint_rows*/ false );
889 }
890
891 // Scale and add to global matrix and/or vector
892 context.get_elem_jacobian() *= scalar;
893 context.get_elem_residual() *= scalar;
894
895 if (assemble_matrix)
896 {
897
898 CouplingMatrix * coupling_matrix = get_dof_map()._dof_coupling;
899 if (!coupling_matrix)
900 {
901 // If we haven't defined a _dof_coupling matrix then just add
902 // the whole matrix
903 input_matrix->add_matrix (context.get_elem_jacobian(),
904 context.get_dof_indices() );
905 }
906 else
907 {
908 // Otherwise we should only add the relevant submatrices
909 for (unsigned int var1=0; var1<n_vars(); var1++)
910 {
911 ConstCouplingRow ccr(var1, *coupling_matrix);
912 for (const auto & var2 : ccr)
913 {
914 unsigned int sub_m = context.get_elem_jacobian( var1, var2 ).m();
915 unsigned int sub_n = context.get_elem_jacobian( var1, var2 ).n();
916 DenseMatrix<Number> sub_jac(sub_m, sub_n);
917 for (unsigned int row=0; row<sub_m; row++)
918 for (unsigned int col=0; col<sub_n; col++)
919 {
920 sub_jac(row,col) = context.get_elem_jacobian( var1, var2 ).el(row,col);
921 }
922 input_matrix->add_matrix (sub_jac,
923 context.get_dof_indices(var1),
924 context.get_dof_indices(var2) );
925 }
926 }
927 }
928
929 }
930
931 if (assemble_vector)
932 input_vector->add_vector (context.get_elem_residual(),
933 context.get_dof_indices() );
934 } // end for (elem)
935
936 } // libmesh_try
937 libmesh_catch(...)
938 {
939 assembly_threw = 1;
940 }
941
942 // Note: regardless of whether any procs threw during assembly (and
943 // thus didn't finish assembling), we should not leave the matrix
944 // and vector in an inconsistent state, since it may be possible to
945 // recover from the exception. Therefore, we close them now. The
946 // assumption here is that the nature of the exception does not
947 // prevent the matrix and vector from still being assembled (albeit
948 // with incomplete data).
949 if (assemble_matrix)
950 input_matrix->close();
951 if (assemble_vector)
952 input_vector->close();
953
954 // Check for exceptions on any procs and if there is one, re-throw
955 // it on all procs.
956 this->comm().max(assembly_threw);
957
958 if (assembly_threw)
959 libmesh_error_msg("Error during assembly in RBConstruction::add_scaled_matrix_and_vector()");
960}
961
963{
964 // Set current_local_solution = vec so that we can access
965 // vec from DGFEMContext during assembly
966 vec.localize
967 (*current_local_solution, this->get_dof_map().get_send_list());
968}
969
971{
972 LOG_SCOPE("truth_assembly()", "RBConstruction");
973
974 const RBParameters & mu = get_parameters();
975
976 this->matrix->zero();
977 this->rhs->zero();
978
979 this->matrix->close();
980 this->rhs->close();
981
982 {
983 // We should have already assembled the matrices
984 // and vectors in the affine expansion, so
985 // just use them
986
987 for (unsigned int q_a=0; q_a<get_rb_theta_expansion().get_n_A_terms(); q_a++)
988 {
990 }
991
992 std::unique_ptr<NumericVector<Number>> temp_vec = NumericVector<Number>::build(this->comm());
993 temp_vec->init (this->n_dofs(), this->n_local_dofs(), false, PARALLEL);
994 for (unsigned int q_f=0; q_f<get_rb_theta_expansion().get_n_F_terms(); q_f++)
995 {
996 *temp_vec = *get_Fq(q_f);
997 temp_vec->scale( get_rb_theta_expansion().eval_F_theta(q_f, mu) );
998 rhs->add(*temp_vec);
999 }
1000 }
1001
1002 this->matrix->close();
1003 this->rhs->close();
1004}
1005
1007 bool apply_dof_constraints)
1008{
1009 input_matrix->zero();
1010
1012 {
1015 input_matrix,
1016 nullptr,
1017 false, /* symmetrize */
1018 apply_dof_constraints);
1019 }
1020 else
1021 {
1022 libmesh_error_msg_if(energy_inner_product_coeffs.size() != get_rb_theta_expansion().get_n_A_terms(),
1023 "Error: invalid number of entries in energy_inner_product_coeffs.");
1024
1025 // We symmetrize below so that we may use the energy inner-product even in cases
1026 // where the A_q are not symmetric.
1027 for (unsigned int q_a=0; q_a<get_rb_theta_expansion().get_n_A_terms(); q_a++)
1028 {
1031 input_matrix,
1032 nullptr,
1033 true, /* symmetrize */
1034 apply_dof_constraints);
1035 }
1036 }
1037}
1038
1040 SparseMatrix<Number> * input_matrix,
1041 bool apply_dof_constraints)
1042{
1043 libmesh_error_msg_if(q >= get_rb_theta_expansion().get_n_A_terms(),
1044 "Error: We must have q < Q_a in assemble_Aq_matrix.");
1045
1046 input_matrix->zero();
1047
1050 input_matrix,
1051 nullptr,
1052 false, /* symmetrize */
1053 apply_dof_constraints);
1054}
1055
1057 unsigned int q_a,
1058 SparseMatrix<Number> * input_matrix,
1059 bool symmetrize)
1060{
1061 LOG_SCOPE("add_scaled_Aq()", "RBConstruction");
1062
1063 libmesh_error_msg_if(q_a >= get_rb_theta_expansion().get_n_A_terms(),
1064 "Error: We must have q < Q_a in add_scaled_Aq.");
1065
1066 if (!symmetrize)
1067 {
1068 input_matrix->add(scalar, *get_Aq(q_a));
1069 input_matrix->close();
1070 }
1071 else
1072 {
1075 input_matrix,
1076 nullptr,
1077 symmetrize);
1078 }
1079}
1080
1082{
1084 {
1085 libMesh::out << "Assembling inner product matrix" << std::endl;
1087 }
1088
1090 {
1091 libMesh::out << "Assembling non-Dirichlet inner product matrix" << std::endl;
1093 }
1094}
1095
1097{
1099 for (unsigned int q_a=0; q_a<get_rb_theta_expansion().get_n_A_terms(); q_a++)
1100 {
1101 libMesh::out << "Assembling affine operator " << (q_a+1) << " of "
1102 << get_rb_theta_expansion().get_n_A_terms() << std::endl;
1103 assemble_Aq_matrix(q_a, get_Aq(q_a));
1104 }
1105
1107 {
1108 for (unsigned int q_a=0; q_a<get_rb_theta_expansion().get_n_A_terms(); q_a++)
1109 {
1110 libMesh::out << "Assembling non-Dirichlet affine operator " << (q_a+1) << " of "
1111 << get_rb_theta_expansion().get_n_A_terms() << std::endl;
1112 assemble_Aq_matrix(q_a, get_non_dirichlet_Aq(q_a), false);
1113 }
1114 }
1115}
1116
1118{
1120 for (unsigned int q_f=0; q_f<get_rb_theta_expansion().get_n_F_terms(); q_f++)
1121 {
1122 libMesh::out << "Assembling affine vector " << (q_f+1) << " of "
1123 << get_rb_theta_expansion().get_n_F_terms() << std::endl;
1124 assemble_Fq_vector(q_f, get_Fq(q_f));
1125 }
1126
1128 {
1129 for (unsigned int q_f=0; q_f<get_rb_theta_expansion().get_n_F_terms(); q_f++)
1130 {
1131 libMesh::out << "Assembling non-Dirichlet affine vector " << (q_f+1) << " of "
1132 << get_rb_theta_expansion().get_n_F_terms() << std::endl;
1133 assemble_Fq_vector(q_f, get_non_dirichlet_Fq(q_f), false);
1134 }
1135 }
1136
1137}
1138
1140 NumericVector<Number> * input_vector,
1141 bool apply_dof_constraints)
1142{
1143 libmesh_error_msg_if(q >= get_rb_theta_expansion().get_n_F_terms(),
1144 "Error: We must have q < Q_f in assemble_Fq_vector.");
1145
1146 input_vector->zero();
1147
1150 nullptr,
1151 input_vector,
1152 false, /* symmetrize */
1153 apply_dof_constraints /* apply_dof_constraints */);
1154}
1155
1157{
1159 for (unsigned int n=0; n<get_rb_theta_expansion().get_n_outputs(); n++)
1160 for (unsigned int q_l=0; q_l<get_rb_theta_expansion().get_n_output_terms(n); q_l++)
1161 {
1162 libMesh::out << "Assembling output vector, (" << (n+1) << "," << (q_l+1)
1163 << ") of (" << get_rb_theta_expansion().get_n_outputs()
1164 << "," << get_rb_theta_expansion().get_n_output_terms(n) << ")"
1165 << std::endl;
1166 get_output_vector(n, q_l)->zero();
1168 nullptr,
1169 get_output_vector(n,q_l),
1170 false, /* symmetrize */
1171 true /* apply_dof_constraints */);
1172 }
1173
1175 {
1176 for (unsigned int n=0; n<get_rb_theta_expansion().get_n_outputs(); n++)
1177 for (unsigned int q_l=0; q_l<get_rb_theta_expansion().get_n_output_terms(n); q_l++)
1178 {
1179 libMesh::out << "Assembling non-Dirichlet output vector, (" << (n+1) << "," << (q_l+1)
1180 << ") of (" << get_rb_theta_expansion().get_n_outputs()
1181 << "," << get_rb_theta_expansion().get_n_output_terms(n) << ")"
1182 << std::endl;
1185 nullptr,
1187 false, /* symmetrize */
1188 false /* apply_dof_constraints */);
1189 }
1190 }
1191}
1192
1193Real RBConstruction::train_reduced_basis(const bool resize_rb_eval_data)
1194{
1195 if(get_RB_training_type() == "Greedy")
1196 {
1197 return train_reduced_basis_with_greedy(resize_rb_eval_data);
1198 }
1199 else if (get_RB_training_type() == "POD")
1200 {
1202 return 0.;
1203 }
1204 else
1205 {
1206 libmesh_error_msg("RB training type not recognized: " + get_RB_training_type());
1207 }
1208
1209 return 0.;
1210}
1211
1213{
1214 LOG_SCOPE("train_reduced_basis_with_greedy()", "RBConstruction");
1215
1216 int count = 0;
1217
1219
1220 // initialize rbe's parameters
1221 rbe.initialize_parameters(*this);
1222
1223 // possibly resize data structures according to Nmax
1224 if (resize_rb_eval_data)
1226
1227 // Clear the Greedy param list
1228 for (auto & plist : rbe.greedy_param_list)
1229 plist.clear();
1230
1231 rbe.greedy_param_list.clear();
1232
1233 Real training_greedy_error = 0.;
1234
1235
1236 // If we are continuing from a previous training run,
1237 // we might already be at the max number of basis functions.
1238 // If so, we can just return.
1239 if (rbe.get_n_basis_functions() >= get_Nmax())
1240 {
1241 libMesh::out << "Maximum number of basis functions reached: Nmax = "
1242 << get_Nmax() << std::endl;
1243 return 0.;
1244 }
1245
1246 // Optionally pre-evaluate the theta functions on the entire (local) training parameter set.
1249
1251 {
1252 // Compute the dual norms of the outputs if we haven't already done so.
1254
1255 // Compute the Fq Riesz representor dual norms if we haven't already done so.
1257 }
1258
1259 libMesh::out << std::endl << "---- Performing Greedy basis enrichment ----" << std::endl;
1260 Real initial_greedy_error = 0.;
1261 bool initial_greedy_error_initialized = false;
1262 while (true)
1263 {
1264 libMesh::out << std::endl << "---- Basis dimension: "
1265 << rbe.get_n_basis_functions() << " ----" << std::endl;
1266
1267 if (count > 0 || (count==0 && use_empty_rb_solve_in_greedy))
1268 {
1269 libMesh::out << "Performing RB solves on training set" << std::endl;
1270 training_greedy_error = compute_max_error_bound();
1271
1272 libMesh::out << "Maximum error bound is " << training_greedy_error << std::endl << std::endl;
1273
1274 // record the initial error
1275 if (!initial_greedy_error_initialized)
1276 {
1277 initial_greedy_error = training_greedy_error;
1278 initial_greedy_error_initialized = true;
1279 }
1280
1281 // Break out of training phase if we have reached Nmax
1282 // or if the training_tolerance is satisfied.
1283 if (greedy_termination_test(training_greedy_error, initial_greedy_error, count))
1284 break;
1285 }
1286
1287 libMesh::out << "Performing truth solve at parameter:" << std::endl;
1289
1290 // Update the list of Greedily selected parameters
1292
1293 // Perform an Offline truth solve for the current parameter
1294 truth_solve(-1);
1295
1297 {
1298 libMesh::out << "Zero basis function encountered hence ending basis enrichment" << std::endl;
1299 break;
1300 }
1301
1302 // Add orthogonal part of the snapshot to the RB space
1303 libMesh::out << "Enriching the RB space" << std::endl;
1305
1306 update_system();
1307
1308 // Check if we've reached Nmax now. We do this before calling
1309 // update_residual_terms() since we can skip that step if we've
1310 // already reached Nmax.
1311 if (rbe.get_n_basis_functions() >= this->get_Nmax())
1312 {
1313 libMesh::out << "Maximum number of basis functions reached: Nmax = "
1314 << get_Nmax() << std::endl;
1315 break;
1316 }
1317
1319 {
1321 }
1322
1323 // Increment counter
1324 count++;
1325 }
1327
1328 return training_greedy_error;
1329}
1330
1331void RBConstruction::enrich_basis_from_rhs_terms(const bool resize_rb_eval_data)
1332{
1333 LOG_SCOPE("enrich_basis_from_rhs_terms()", "RBConstruction");
1334
1335 // initialize rb_eval's parameters
1337
1338 // possibly resize data structures according to Nmax
1339 if (resize_rb_eval_data)
1340 {
1342 }
1343
1344 libMesh::out << std::endl << "---- Enriching basis from rhs terms ----" << std::endl;
1345
1347
1348 for (unsigned int q_f=0; q_f<get_rb_theta_expansion().get_n_F_terms(); q_f++)
1349 {
1350 libMesh::out << std::endl << "Performing truth solve with rhs from rhs term " << q_f << std::endl;
1351
1352 *rhs = *get_Fq(q_f);
1353
1354 if (rhs->l2_norm() == 0)
1355 {
1356 // Skip enrichment if the rhs is zero
1357 continue;
1358 }
1359
1360 // truth_assembly assembles into matrix and rhs, so use those for the solve
1362 {
1363 // If extra_linear_solver has been initialized, then we use it for the
1364 // truth solves.
1366
1369 }
1370 else
1371 {
1373
1376 }
1377
1378 // Debugging: enable this code to print the rhs that was used in
1379 // the most recent truth solve to a uniquely-named file.
1380#if 0
1381 {
1382 char temp_file[] = "truth_rhs_XXXXXX.dat";
1383 int fd = mkstemps(temp_file, 4);
1384 if (fd != -1)
1385 {
1386 libMesh::out << "Writing truth system rhs to file: " << temp_file << std::endl;
1387 rhs->print_matlab(std::string(temp_file));
1388 }
1389 }
1390#endif // 0
1391
1392 // Debugging: enable this code to print the most recent truth
1393 // solution to a uniquely-named file.
1394#ifdef LIBMESH_HAVE_EXODUS_API
1395#if 0
1396 {
1397 // Note: mkstemps creates a file and returns an open file descriptor to it.
1398 // The filename is created from a template which must have 6 'X' characters followed
1399 // by a suffix having the specified length (in this case 4, for ".exo").
1400 char temp_file[] = "truth_XXXXXX.exo";
1401 int fd = mkstemps(temp_file, 4);
1402 if (fd != -1)
1403 {
1404 libMesh::out << "Writing truth solution to file: " << temp_file << std::endl;
1405 ExodusII_IO exo_io(this->get_mesh());
1406 std::set<std::string> system_names = {this->name()};
1407 exo_io.write_equation_systems(std::string(temp_file),
1408 this->get_equation_systems(), &system_names);
1409 }
1410 }
1411#endif // 0
1412#endif // LIBMESH_HAVE_EXODUS_API
1413
1414 // Call user-defined post-processing routines on the truth solution.
1416
1417 // Add orthogonal part of the snapshot to the RB space
1418 libMesh::out << "Enriching the RB space" << std::endl;
1420
1421 update_system();
1422 }
1423}
1424
1426{
1427 // We need to use the same training set on all processes so that
1428 // the truth solves below work correctly in parallel.
1429 libmesh_error_msg_if(!serial_training_set, "We must use a serial training set with POD");
1430 libmesh_error_msg_if(get_rb_evaluation().get_n_basis_functions() > 0, "Basis should not already be initialized");
1431
1434
1435 // Storage for the POD snapshots
1436 unsigned int n_snapshots = get_n_training_samples();
1437
1438 if (get_n_params() == 0)
1439 {
1440 // In this case we should have generated an empty training set
1441 // so assert this
1442 libmesh_assert(n_snapshots == 0);
1443
1444 // If we have no parameters, then we should do exactly one "truth solve"
1445 n_snapshots = 1;
1446 }
1447
1448 std::vector<std::unique_ptr<NumericVector<Number>>> POD_snapshots(n_snapshots);
1449 for (unsigned int i=0; i<n_snapshots; i++)
1450 {
1451 POD_snapshots[i] = NumericVector<Number>::build(this->comm());
1452 POD_snapshots[i]->init (this->n_dofs(), this->n_local_dofs(), false, PARALLEL);
1453 }
1454
1455 // We use the same training set on all processes
1456 libMesh::out << std::endl;
1457 for (unsigned int i=0; i<n_snapshots; i++)
1458 {
1459 if (get_n_params() > 0)
1460 {
1462 }
1463
1464 libMesh::out << "Truth solve " << (i+1) << " of " << n_snapshots << std::endl;
1465
1466 truth_solve(-1);
1467
1468 *POD_snapshots[i] = *solution;
1469 }
1470 libMesh::out << std::endl;
1471
1473 {
1474 libMesh::out << "Normalizing solution snapshots" << std::endl;
1475 for (unsigned int i=0; i<n_snapshots; i++)
1476 {
1478 *inner_product_storage_vector, *POD_snapshots[i]);
1479 Real norm = std::sqrt(std::real(POD_snapshots[i]->dot(*inner_product_storage_vector)));
1480
1481 if (norm > 0.)
1482 POD_snapshots[i]->scale(1./norm);
1483 }
1484 }
1485
1486 // Set up the "correlation matrix"
1487 DenseMatrix<Number> correlation_matrix(n_snapshots,n_snapshots);
1488 for (unsigned int i=0; i<n_snapshots; i++)
1489 {
1491 *inner_product_storage_vector, *POD_snapshots[i]);
1492
1493 for (unsigned int j=0; j<=i; j++)
1494 {
1495 Number inner_prod = (POD_snapshots[j]->dot(*inner_product_storage_vector));
1496
1497 correlation_matrix(i,j) = inner_prod;
1498 if(i != j)
1499 {
1500 correlation_matrix(j,i) = libmesh_conj(inner_prod);
1501 }
1502 }
1503 }
1504
1505 // compute SVD of correlation matrix
1506 DenseVector<Real> sigma( n_snapshots );
1507 DenseMatrix<Number> U( n_snapshots, n_snapshots );
1508 DenseMatrix<Number> VT( n_snapshots, n_snapshots );
1509 correlation_matrix.svd(sigma, U, VT );
1510
1511 if (sigma(0) == 0.)
1512 return;
1513
1514 // Add dominant vectors from the POD as basis functions.
1515 unsigned int j = 0;
1516 while (true)
1517 {
1518 if (j >= get_Nmax() || j >= n_snapshots)
1519 {
1520 libMesh::out << "Maximum number of basis functions (" << j << ") reached." << std::endl;
1521 break;
1522 }
1523
1524 // The "energy" error in the POD approximation is determined by the first omitted
1525 // singular value, i.e. sigma(j). We normalize by sigma(0), which gives the total
1526 // "energy", in order to obtain a relative error.
1527 const Real rel_err = std::sqrt(sigma(j)) / std::sqrt(sigma(0));
1528
1529 libMesh::out << "Number of basis functions: " << j
1530 << ", POD error norm: " << rel_err << std::endl;
1531
1532 if (rel_err < this->rel_training_tolerance)
1533 {
1534 libMesh::out << "Training tolerance reached." << std::endl;
1535 break;
1536 }
1537
1538 std::unique_ptr< NumericVector<Number> > v = POD_snapshots[j]->zero_clone();
1539 for ( unsigned int i=0; i<n_snapshots; ++i )
1540 {
1541 v->add( U.el(i, j), *POD_snapshots[i] );
1542 }
1543
1544 Real norm_v = std::sqrt(sigma(j));
1545 v->scale( 1./norm_v );
1546
1547 get_rb_evaluation().basis_functions.emplace_back( std::move(v) );
1548
1549 j++;
1550 }
1551 libMesh::out << std::endl;
1552
1554 update_system();
1555
1556 // We now compute all terms required to evaluate the RB error indicator.
1557 // Unlike in the case of the RB Greedy algorithm, for the POD approach
1558 // we do not need this data in order to compute the basis. However, we
1559 // do need this data in order to evaluate error indicator quantities in
1560 // the Online stage, so we compute it now so that it can be saved in
1561 // the training data.
1563}
1564
1566 Real initial_error,
1567 int)
1568{
1569 if (abs_greedy_error < this->abs_training_tolerance)
1570 {
1571 libMesh::out << "Absolute error tolerance reached." << std::endl;
1572 return true;
1573 }
1574
1575 Real rel_greedy_error = abs_greedy_error/initial_error;
1576 if (rel_greedy_error < this->rel_training_tolerance)
1577 {
1578 libMesh::out << "Relative error tolerance reached." << std::endl;
1579 return true;
1580 }
1581
1583
1584 if (rbe.get_n_basis_functions() >= this->get_Nmax())
1585 {
1586 libMesh::out << "Maximum number of basis functions reached: Nmax = "
1587 << get_Nmax() << std::endl;
1588 return true;
1589 }
1590
1592 for (auto & plist : rbe.greedy_param_list)
1593 if (plist == get_parameters())
1594 {
1595 libMesh::out << "Exiting greedy because the same parameters were selected twice" << std::endl;
1596 return true;
1597 }
1598
1599 return false;
1600}
1601
1606
1608{
1609 libmesh_error_msg_if(i >= get_rb_evaluation().greedy_param_list.size(),
1610 "Error: Argument in RBConstruction::get_greedy_parameter is too large.");
1611
1613}
1614
1616{
1617 LOG_SCOPE("truth_solve()", "RBConstruction");
1618
1620 {
1621 _untransformed_solution = solution->zero_clone();
1622 }
1623
1625
1626 // truth_assembly assembles into matrix and rhs, so use those for the solve
1628 {
1629 // If extra_linear_solver has been initialized, then we use it for the
1630 // truth solves.
1632
1635 }
1636 else
1637 {
1639
1642 }
1643
1645 {
1647 }
1648
1649 // Call user-defined post-processing routines on the truth solution.
1651
1652 const RBParameters & mu = get_parameters();
1653
1654 for (unsigned int n=0; n<get_rb_theta_expansion().get_n_outputs(); n++)
1655 {
1656 truth_outputs[n] = 0.;
1657 for (unsigned int q_l=0; q_l<get_rb_theta_expansion().get_n_output_terms(n); q_l++)
1659 get_output_vector(n,q_l)->dot(*solution);
1660 }
1661
1662#ifdef LIBMESH_HAVE_EXODUS_API
1663 if (plot_solution > 0)
1664 {
1665 ExodusII_IO exo_io(this->get_mesh());
1666 std::set<std::string> system_names = {this->name()};
1667 exo_io.write_equation_systems("truth.exo", this->get_equation_systems(), &system_names);
1668 }
1669#else
1670 libmesh_ignore(plot_solution);
1671#endif
1672
1673 // Get the X norm of the truth solution
1674 // Useful for normalizing our true error data
1676 Number truth_X_norm = std::sqrt(inner_product_storage_vector->dot(*solution));
1677
1678 return libmesh_real(truth_X_norm);
1679}
1680
1681bool RBConstruction::is_serial_training_type(const std::string & RB_training_type_in)
1682{
1683 return (RB_training_type_in == "POD");
1684}
1685
1686void RBConstruction::set_RB_training_type(const std::string & RB_training_type_in)
1687{
1688 this->RB_training_type = RB_training_type_in;
1689
1690 if(is_serial_training_type(RB_training_type_in))
1691 {
1692 // We need to use a serial training set (so that the training
1693 // set is the same on all processes) if we're using POD
1694 this->serial_training_set = true;
1695 }
1696}
1697
1698const std::string & RBConstruction::get_RB_training_type() const
1699{
1700 return RB_training_type;
1701}
1702
1703void RBConstruction::set_Nmax(unsigned int Nmax_in)
1704{
1705 this->Nmax = Nmax_in;
1706}
1707
1709{
1710 LOG_SCOPE("load_basis_function()", "RBConstruction");
1711
1712 libmesh_assert_less (i, get_rb_evaluation().get_n_basis_functions());
1713
1715
1716 this->update();
1717}
1718
1720{
1721 LOG_SCOPE("enrich_RB_space()", "RBConstruction");
1722
1723 auto new_bf = solution->clone();
1724
1725 std::unique_ptr<NumericVector<Number>> new_untransformed_bf;
1727 {
1728 new_untransformed_bf = _untransformed_solution->clone();
1729 libmesh_assert_equal_to(_untransformed_basis_functions.size(), get_rb_evaluation().get_n_basis_functions());
1730 }
1731
1732 for (unsigned int index=0; index<get_rb_evaluation().get_n_basis_functions(); index++)
1733 {
1735
1736 Number scalar =
1737 inner_product_storage_vector->dot(get_rb_evaluation().get_basis_function(index));
1738 new_bf->add(-scalar, get_rb_evaluation().get_basis_function(index));
1739
1741 {
1742 new_untransformed_bf->add(-scalar, *_untransformed_basis_functions[index]);
1743 }
1744 }
1745
1746 // Normalize new_bf
1748 Number new_bf_norm = std::sqrt( inner_product_storage_vector->dot(*new_bf) );
1749
1750 if (new_bf_norm == 0.)
1751 {
1752 new_bf->zero(); // avoid potential nan's
1753
1755 {
1756 new_untransformed_bf->zero();
1757 }
1758 }
1759 else
1760 {
1761 new_bf->scale(1./new_bf_norm);
1762
1764 {
1765 new_untransformed_bf->scale(1./new_bf_norm);
1766 }
1767 }
1768
1769 // load the new basis function into the basis_functions vector.
1770 get_rb_evaluation().basis_functions.emplace_back( std::move(new_bf) );
1771
1773 {
1774 _untransformed_basis_functions.emplace_back( std::move(new_untransformed_bf) );
1775 }
1776}
1777
1779{
1780 libMesh::out << "Updating RB matrices" << std::endl;
1782}
1783
1785{
1787
1788 Real error_bound = 0.;
1790 {
1791 // Obtain the pre-evaluated theta functions from the current training parameter index
1792 const auto & evaluated_thetas = get_evaluated_thetas(get_current_training_parameter_index());
1793 error_bound = get_rb_evaluation().rb_solve(get_rb_evaluation().get_n_basis_functions(),
1794 &evaluated_thetas);
1795 }
1796 else
1797 error_bound = get_rb_evaluation().rb_solve(get_rb_evaluation().get_n_basis_functions());
1798
1799
1801 {
1802 Real error_bound_normalization = get_rb_evaluation().get_error_bound_normalization();
1803
1804 if ((error_bound < abs_training_tolerance) ||
1805 (error_bound_normalization < abs_training_tolerance))
1806 {
1807 // We don't want to normalize this error bound if the bound or the
1808 // normalization value are below the absolute tolerance. Hence do nothing
1809 // in this case.
1810 }
1811 else
1812 error_bound /= error_bound_normalization;
1813 }
1814
1815 return error_bound;
1816}
1817
1818void RBConstruction::recompute_all_residual_terms(bool compute_inner_products)
1819{
1820 // Compute the basis independent terms
1822 compute_Fq_representor_innerprods(compute_inner_products);
1823
1824 // and all the basis dependent terms
1825 unsigned int saved_delta_N = delta_N;
1827
1828 update_residual_terms(compute_inner_products);
1829
1830 delta_N = saved_delta_N;
1831}
1832
1834{
1835 LOG_SCOPE("compute_max_error_bound()", "RBConstruction");
1836
1837 // Treat the case with no parameters in a special way
1838 if (get_n_params() == 0)
1839 {
1840 Real max_val;
1841 if (std::numeric_limits<Real>::has_infinity)
1842 {
1843 max_val = std::numeric_limits<Real>::infinity();
1844 }
1845 else
1846 {
1847 max_val = std::numeric_limits<Real>::max();
1848 }
1849
1850 // Make sure we do at least one solve, but otherwise return a zero error bound
1851 // when we have no parameters
1852 return (get_rb_evaluation().get_n_basis_functions() == 0) ? max_val : 0.;
1853 }
1854
1856
1857 // keep track of the maximum error
1858 unsigned int max_err_index = 0;
1859 Real max_err = 0.;
1860
1862 for (unsigned int i=0; i<get_local_n_training_samples(); i++)
1863 {
1864 // Load training parameter i, this is only loaded
1865 // locally since the RB solves are local.
1866 set_params_from_training_set( first_index+i );
1867
1868 // In case we pre-evaluate the theta functions,
1869 // also keep track of the current training parameter index.
1872
1873
1875
1876 if (training_error_bounds[i] > max_err)
1877 {
1878 max_err_index = i;
1879 max_err = training_error_bounds[i];
1880 }
1881 }
1882
1883 std::pair<numeric_index_type, Real> error_pair(first_index+max_err_index, max_err);
1884 get_global_max_error_pair(this->comm(),error_pair);
1885
1886 // If we have a serial training set (i.e. a training set that is the same on all processors)
1887 // just set the parameters on all processors
1889 {
1890 set_params_from_training_set( error_pair.first );
1891 }
1892 // otherwise, broadcast the parameter that produced the maximum error
1893 else
1894 {
1895 unsigned int root_id=0;
1896 if ((get_first_local_training_index() <= error_pair.first) &&
1897 (error_pair.first < get_last_local_training_index()))
1898 {
1899 set_params_from_training_set( error_pair.first );
1900 root_id = this->processor_id();
1901 }
1902
1903 this->comm().sum(root_id); // root_id is only non-zero on one processor
1904 broadcast_parameters(root_id);
1905 }
1906
1907 return error_pair.second;
1908}
1909
1911{
1912 LOG_SCOPE("update_RB_system_matrices()", "RBConstruction");
1913
1914 unsigned int RB_size = get_rb_evaluation().get_n_basis_functions();
1915
1916 std::unique_ptr<NumericVector<Number>> temp = NumericVector<Number>::build(this->comm());
1917 temp->init (this->n_dofs(), this->n_local_dofs(), false, PARALLEL);
1918
1919 for (unsigned int q_f=0; q_f<get_rb_theta_expansion().get_n_F_terms(); q_f++)
1920 {
1921 for (unsigned int i=(RB_size-delta_N); i<RB_size; i++)
1922 {
1924 }
1925 }
1926
1927 for (unsigned int i=(RB_size-delta_N); i<RB_size; i++)
1928 {
1929 for (unsigned int n=0; n<get_rb_theta_expansion().get_n_outputs(); n++)
1930 for (unsigned int q_l=0; q_l<get_rb_theta_expansion().get_n_output_terms(n); q_l++)
1931 {
1934 }
1935
1936 for (unsigned int j=0; j<RB_size; j++)
1937 {
1938 Number value = 0.;
1939
1941 {
1942 // Compute reduced inner_product_matrix
1943 temp->zero();
1945
1946 value = temp->dot( get_rb_evaluation().get_basis_function(i) );
1948 if (i!=j)
1949 {
1950 // The inner product matrix is assumed
1951 // to be hermitian
1953 }
1954 }
1955
1956 for (unsigned int q_a=0; q_a<get_rb_theta_expansion().get_n_A_terms(); q_a++)
1957 {
1958 // Compute reduced Aq matrix
1959 temp->zero();
1960 get_non_dirichlet_Aq_if_avail(q_a)->vector_mult(*temp, get_rb_evaluation().get_basis_function(j));
1961
1962 value = (*temp).dot(get_rb_evaluation().get_basis_function(i));
1963 get_rb_evaluation().RB_Aq_vector[q_a](i,j) = value;
1964
1965 if (i!=j)
1966 {
1967 temp->zero();
1968 get_non_dirichlet_Aq_if_avail(q_a)->vector_mult(*temp, get_rb_evaluation().get_basis_function(i));
1969
1970 value = (*temp).dot(get_rb_evaluation().get_basis_function(j));
1971 get_rb_evaluation().RB_Aq_vector[q_a](j,i) = value;
1972 }
1973 }
1974 }
1975 }
1976}
1977
1978
1979void RBConstruction::update_residual_terms(bool compute_inner_products)
1980{
1981 LOG_SCOPE("update_residual_terms()", "RBConstruction");
1982
1983 libMesh::out << "Updating RB residual terms" << std::endl;
1984
1985 unsigned int RB_size = get_rb_evaluation().get_n_basis_functions();
1986
1988 {
1989 libmesh_assert_equal_to(_untransformed_basis_functions.size(), get_rb_evaluation().get_n_basis_functions());
1990 }
1991
1992 for (unsigned int q_a=0; q_a<get_rb_theta_expansion().get_n_A_terms(); q_a++)
1993 {
1994 for (unsigned int i=(RB_size-delta_N); i<RB_size; i++)
1995 {
1996 // Initialize the vector in which we'll store the representor
1997 if (!get_rb_evaluation().Aq_representor[q_a][i])
1998 {
2000 get_rb_evaluation().Aq_representor[q_a][i]->init(this->n_dofs(), this->n_local_dofs(), false, PARALLEL);
2001 }
2002
2003 libmesh_assert(get_rb_evaluation().Aq_representor[q_a][i]->size() == this->n_dofs() &&
2004 get_rb_evaluation().Aq_representor[q_a][i]->local_size() == this->n_local_dofs() );
2005
2006 rhs->zero();
2008 {
2009 get_Aq(q_a)->vector_mult(*rhs, get_rb_evaluation().get_basis_function(i));
2010 }
2011 else
2012 {
2014 }
2015 rhs->scale(-1.);
2016
2017 if (!is_quiet())
2018 {
2019 libMesh::out << "Starting solve [q_a][i]=[" << q_a <<"]["<< i << "] in RBConstruction::update_residual_terms() at "
2020 << Utility::get_timestamp() << std::endl;
2021 }
2022
2024
2027
2028 if (!is_quiet())
2029 {
2030 libMesh::out << "Finished solve [q_a][i]=[" << q_a <<"]["<< i << "] in RBConstruction::update_residual_terms() at "
2031 << Utility::get_timestamp() << std::endl;
2032 libMesh::out << this->n_linear_iterations() << " iterations, final residual "
2033 << this->final_linear_residual() << std::endl;
2034 }
2035
2036 // Store the representor
2038 }
2039 }
2040
2041 // Now compute and store the inner products (if requested)
2042 if (compute_inner_products)
2043 {
2044
2045 for (unsigned int q_f=0; q_f<get_rb_theta_expansion().get_n_F_terms(); q_f++)
2046 {
2048
2049 for (unsigned int q_a=0; q_a<get_rb_theta_expansion().get_n_A_terms(); q_a++)
2050 {
2051 for (unsigned int i=(RB_size-delta_N); i<RB_size; i++)
2052 {
2054 inner_product_storage_vector->dot(*get_rb_evaluation().Aq_representor[q_a][i]);
2055 }
2056 }
2057 }
2058
2059 unsigned int q=0;
2060 for (unsigned int q_a1=0; q_a1<get_rb_theta_expansion().get_n_A_terms(); q_a1++)
2061 {
2062 for (unsigned int q_a2=q_a1; q_a2<get_rb_theta_expansion().get_n_A_terms(); q_a2++)
2063 {
2064 for (unsigned int i=(RB_size-delta_N); i<RB_size; i++)
2065 {
2066 for (unsigned int j=0; j<RB_size; j++)
2067 {
2070 inner_product_storage_vector->dot(*get_rb_evaluation().Aq_representor[q_a1][i]);
2071
2072 if (i != j)
2073 {
2076 inner_product_storage_vector->dot(*get_rb_evaluation().Aq_representor[q_a1][j]);
2077 }
2078 }
2079 }
2080 q++;
2081 }
2082 }
2083 } // end if (compute_inner_products)
2084}
2085
2090
2092{
2093 // Skip calculations if we've already computed the output dual norms
2095 {
2096 // Short circuit if we don't have any outputs
2097 if (get_rb_theta_expansion().get_n_outputs() == 0)
2098 {
2100 return;
2101 }
2102
2103 // Only log if we get to here
2104 LOG_SCOPE("compute_output_dual_innerprods()", "RBConstruction");
2105
2106 libMesh::out << "Compute output dual inner products" << std::endl;
2107
2108 // Find out the largest value of Q_l
2109 unsigned int max_Q_l = 0;
2110 for (unsigned int n=0; n<get_rb_theta_expansion().get_n_outputs(); n++)
2111 max_Q_l = (get_rb_theta_expansion().get_n_output_terms(n) > max_Q_l) ? get_rb_theta_expansion().get_n_output_terms(n) : max_Q_l;
2112
2113 std::vector<std::unique_ptr<NumericVector<Number>>> L_q_representor(max_Q_l);
2114 for (unsigned int q=0; q<max_Q_l; q++)
2115 {
2116 L_q_representor[q] = NumericVector<Number>::build(this->comm());
2117 L_q_representor[q]->init (this->n_dofs(), this->n_local_dofs(), false, PARALLEL);
2118 }
2119
2120 for (unsigned int n=0; n<get_rb_theta_expansion().get_n_outputs(); n++)
2121 {
2122 for (unsigned int q_l=0; q_l<get_rb_theta_expansion().get_n_output_terms(n); q_l++)
2123 {
2124 rhs->zero();
2125 rhs->add(1., *get_output_vector(n,q_l));
2126
2127 if (!is_quiet())
2128 libMesh::out << "Starting solve n=" << n << ", q_l=" << q_l
2129 << " in RBConstruction::compute_output_dual_innerprods() at "
2130 << Utility::get_timestamp() << std::endl;
2131
2132 // Use the main linear solver here instead of the inner_product solver, since
2133 // get_matrix_for_output_dual_solves() may not return the inner product matrix.
2135
2136 // We possibly perform multiple solves here with the same matrix, hence
2137 // set reuse_preconditioner(true) (and set it back to false again below
2138 // at the end of this function).
2139 linear_solver->reuse_preconditioner(true);
2140
2143
2144 if (!is_quiet())
2145 {
2146 libMesh::out << "Finished solve n=" << n << ", q_l=" << q_l
2147 << " in RBConstruction::compute_output_dual_innerprods() at "
2148 << Utility::get_timestamp() << std::endl;
2149
2151 << " iterations, final residual "
2152 << this->final_linear_residual() << std::endl;
2153 }
2154
2155 *L_q_representor[q_l] = *solution;
2156 }
2157
2158 unsigned int q=0;
2159 for (unsigned int q_l1=0; q_l1<get_rb_theta_expansion().get_n_output_terms(n); q_l1++)
2160 {
2162
2163 for (unsigned int q_l2=q_l1; q_l2<get_rb_theta_expansion().get_n_output_terms(n); q_l2++)
2164 {
2165 output_dual_innerprods[n][q] = L_q_representor[q_l2]->dot(*inner_product_storage_vector);
2166 libMesh::out << "output_dual_innerprods[" << n << "][" << q << "] = " << output_dual_innerprods[n][q] << std::endl;
2167
2168 q++;
2169 }
2170 }
2171 }
2172
2173 // We may not need to use linear_solver again (e.g. this would happen if we use
2174 // extra_linear_solver for the truth_solves). As a result, let's clear linear_solver
2175 // to release any memory it may be taking up. If we do need it again, it will
2176 // be initialized when necessary.
2177 linear_solver->clear();
2178 linear_solver->reuse_preconditioner(false);
2179
2181 }
2182
2184}
2185
2187{
2188
2189 // Skip calculations if we've already computed the Fq_representors
2191 {
2192 // Only log if we get to here
2193 LOG_SCOPE("compute_Fq_representor_innerprods()", "RBConstruction");
2194
2195 for (unsigned int q_f=0; q_f<get_rb_theta_expansion().get_n_F_terms(); q_f++)
2196 {
2197 if (!Fq_representor[q_f])
2198 {
2200 Fq_representor[q_f]->init (this->n_dofs(), this->n_local_dofs(), false, PARALLEL);
2201 }
2202
2203 libmesh_assert(Fq_representor[q_f]->size() == this->n_dofs() &&
2204 Fq_representor[q_f]->local_size() == this->n_local_dofs() );
2205
2206 rhs->zero();
2207 rhs->add(1., *get_Fq(q_f));
2208
2209 if (!is_quiet())
2210 libMesh::out << "Starting solve q_f=" << q_f
2211 << " in RBConstruction::update_residual_terms() at "
2212 << Utility::get_timestamp() << std::endl;
2213
2215
2218
2219 if (!is_quiet())
2220 {
2221 libMesh::out << "Finished solve q_f=" << q_f
2222 << " in RBConstruction::update_residual_terms() at "
2223 << Utility::get_timestamp() << std::endl;
2224
2226 << " iterations, final residual "
2227 << this->final_linear_residual() << std::endl;
2228 }
2229
2230 *Fq_representor[q_f] = *solution;
2231 }
2232
2233 if (compute_inner_products)
2234 {
2235 unsigned int q=0;
2236
2237 for (unsigned int q_f1=0; q_f1<get_rb_theta_expansion().get_n_F_terms(); q_f1++)
2238 {
2240
2241 for (unsigned int q_f2=q_f1; q_f2<get_rb_theta_expansion().get_n_F_terms(); q_f2++)
2242 {
2244
2245 q++;
2246 }
2247 }
2248 } // end if (compute_inner_products)
2249
2251 }
2252
2254}
2255
2257{
2258 LOG_SCOPE("load_rb_solution()", "RBConstruction");
2259
2260 solution->zero();
2261
2262 libmesh_error_msg_if(get_rb_evaluation().RB_solution.size() > get_rb_evaluation().get_n_basis_functions(),
2263 "ERROR: System contains " << get_rb_evaluation().get_n_basis_functions() << " basis functions."
2264 << " RB_solution vector contains " << get_rb_evaluation().RB_solution.size() << " entries."
2265 << " RB_solution in RBConstruction::load_rb_solution is too long!");
2266
2267 for (auto i : make_range(get_rb_evaluation().RB_solution.size()))
2269
2270 update();
2271}
2272
2273// The slow (but simple, non-error prone) way to compute the residual dual norm
2274// Useful for error checking
2276{
2277 LOG_SCOPE("compute_residual_dual_norm_slow()", "RBConstruction");
2278
2279 // Put the residual in rhs in order to compute the norm of the Riesz representor
2280 // Note that this only works in serial since otherwise each processor will
2281 // have a different parameter value during the Greedy training.
2282
2283 std::unique_ptr<NumericVector<Number>> RB_sol = NumericVector<Number>::build(comm());
2284 RB_sol->init (this->n_dofs(), this->n_local_dofs(), false, PARALLEL);
2285
2286 std::unique_ptr<NumericVector<Number>> temp = NumericVector<Number>::build(comm());
2287 temp->init (this->n_dofs(), this->n_local_dofs(), false, PARALLEL);
2288
2290 {
2291 libmesh_assert_equal_to(_untransformed_basis_functions.size(), get_rb_evaluation().get_n_basis_functions());
2292 }
2293
2294 for (unsigned int i=0; i<N; i++)
2295 {
2297 {
2298 RB_sol->add(get_rb_evaluation().RB_solution(i), get_rb_evaluation().get_basis_function(i));
2299 }
2300 else
2301 {
2302 RB_sol->add(get_rb_evaluation().RB_solution(i), *_untransformed_basis_functions[i]);
2303 }
2304 }
2305
2306 this->truth_assembly();
2307 matrix->vector_mult(*temp, *RB_sol);
2308 rhs->add(-1., *temp);
2309
2310 // Then solve to get the Reisz representor
2311 matrix->zero();
2313
2316 Number slow_residual_norm_sq = solution->dot(*inner_product_storage_vector);
2317
2318 return std::sqrt( libmesh_real(slow_residual_norm_sq) );
2319}
2320
2322{
2323 libmesh_error_msg_if(!store_dirichlet_operators,
2324 "Error: Must have store_dirichlet_operators==true to access inner_product_matrix.");
2325 return inner_product_matrix.get();
2326}
2327
2329{
2330 libmesh_error_msg_if(!store_dirichlet_operators,
2331 "Error: Must have store_dirichlet_operators==true to access inner_product_matrix.");
2332 return inner_product_matrix.get();
2333}
2334
2336{
2337 libmesh_error_msg_if(!store_non_dirichlet_operators,
2338 "Error: Must have store_non_dirichlet_operators==true to access non_dirichlet_inner_product_matrix.");
2339
2341}
2342
2344{
2345 libmesh_error_msg_if(!store_non_dirichlet_operators,
2346 "Error: Must have store_non_dirichlet_operators==true to access non_dirichlet_inner_product_matrix.");
2347
2349}
2350
2360
2370
2372{
2373 libmesh_error_msg_if(!store_dirichlet_operators,
2374 "Error: Must have store_dirichlet_operators==true to access non_dirichlet_Aq.");
2375
2376 libmesh_error_msg_if(q >= get_rb_theta_expansion().get_n_A_terms(),
2377 "Error: We must have q < Q_a in get_Aq.");
2378
2379 return Aq_vector[q].get();
2380}
2381
2383{
2384 libmesh_error_msg_if(!store_non_dirichlet_operators,
2385 "Error: Must have store_non_dirichlet_operators==true to access non_dirichlet_Aq.");
2386
2387 libmesh_error_msg_if(q >= get_rb_theta_expansion().get_n_A_terms(),
2388 "Error: We must have q < Q_a in get_Aq.");
2389
2390 return non_dirichlet_Aq_vector[q].get();
2391}
2392
2394{
2396 {
2397 return get_non_dirichlet_Aq(q);
2398 }
2399
2400 return get_Aq(q);
2401}
2402
2404{
2405 libmesh_error_msg_if(!store_dirichlet_operators,
2406 "Error: Must have store_dirichlet_operators==true to access non_dirichlet_Fq.");
2407
2408 libmesh_error_msg_if(q >= get_rb_theta_expansion().get_n_F_terms(),
2409 "Error: We must have q < Q_f in get_Fq.");
2410
2411 return Fq_vector[q].get();
2412}
2413
2415{
2416 libmesh_error_msg_if(!store_non_dirichlet_operators,
2417 "Error: Must have store_non_dirichlet_operators==true to access non_dirichlet_Fq.");
2418
2419 libmesh_error_msg_if(q >= get_rb_theta_expansion().get_n_F_terms(),
2420 "Error: We must have q < Q_f in get_Fq.");
2421
2422 return non_dirichlet_Fq_vector[q].get();
2423}
2424
2426{
2428 {
2429 return get_non_dirichlet_Fq(q);
2430 }
2431
2432 return get_Fq(q);
2433}
2434
2436{
2437 libmesh_error_msg_if((n >= get_rb_theta_expansion().get_n_outputs()) ||
2438 (q_l >= get_rb_theta_expansion().get_n_output_terms(n)),
2439 "Error: We must have n < n_outputs and "
2440 "q_l < get_rb_theta_expansion().get_n_output_terms(n) in get_output_vector.");
2441
2442 return outputs_vector[n][q_l].get();
2443}
2444
2446{
2447 libmesh_error_msg_if((n >= get_rb_theta_expansion().get_n_outputs()) ||
2448 (q_l >= get_rb_theta_expansion().get_n_output_terms(n)),
2449 "Error: We must have n < n_outputs and "
2450 "q_l < get_rb_theta_expansion().get_n_output_terms(n) in get_non_dirichlet_output_vector.");
2451
2452 return non_dirichlet_outputs_vector[n][q_l].get();
2453}
2454
2455void RBConstruction::get_all_matrices(std::map<std::string, SparseMatrix<Number> *> & all_matrices)
2456{
2457 all_matrices.clear();
2458
2460 all_matrices["inner_product"] = get_inner_product_matrix();
2461
2463 all_matrices["non_dirichlet_inner_product"] = get_non_dirichlet_inner_product_matrix();
2464
2465 for (unsigned int q_a=0; q_a<get_rb_theta_expansion().get_n_A_terms(); q_a++)
2466 {
2467 std::stringstream matrix_name;
2468 matrix_name << "A" << q_a;
2469
2471 all_matrices[matrix_name.str()] = get_Aq(q_a);
2472
2474 {
2475 matrix_name << "_non_dirichlet";
2476 all_matrices[matrix_name.str()] = get_non_dirichlet_Aq(q_a);
2477 }
2478 }
2479}
2480
2481void RBConstruction::get_all_vectors(std::map<std::string, NumericVector<Number> *> & all_vectors)
2482{
2483 all_vectors.clear();
2484
2485 get_output_vectors(all_vectors);
2486
2487 for (unsigned int q_f=0; q_f<get_rb_theta_expansion().get_n_F_terms(); q_f++)
2488 {
2489 std::stringstream F_vector_name;
2490 F_vector_name << "F" << q_f;
2491
2493 all_vectors[F_vector_name.str()] = get_Fq(q_f);
2494
2496 {
2497 F_vector_name << "_non_dirichlet";
2498 all_vectors[F_vector_name.str()] = get_non_dirichlet_Fq(q_f);
2499 }
2500 }
2501}
2502
2503void RBConstruction::get_output_vectors(std::map<std::string, NumericVector<Number> *> & output_vectors)
2504{
2505 output_vectors.clear();
2506
2507 for (unsigned int n=0; n<get_rb_theta_expansion().get_n_outputs(); n++)
2508 for (unsigned int q_l=0; q_l<get_rb_theta_expansion().get_n_output_terms(n); q_l++)
2509 {
2510 std::stringstream output_name;
2512 {
2513 output_name << "output_" << n << "_"<< q_l;
2514 output_vectors[output_name.str()] = get_output_vector(n,q_l);
2515 }
2516
2518 {
2519 output_name << "_non_dirichlet";
2520 output_vectors[output_name.str()] = get_non_dirichlet_output_vector(n,q_l);
2521 }
2522 }
2523}
2524
2525#ifdef LIBMESH_ENABLE_DIRICHLET
2526
2528{
2529 ZeroFunction<> zf;
2530
2531 std::set<boundary_id_type> dirichlet_ids;
2532 std::vector<unsigned int> variables;
2533
2534 // The DirichletBoundary constructor clones zf, so it's OK that zf is only in local scope
2535 return std::make_unique<DirichletBoundary>(dirichlet_ids, variables, &zf);
2536}
2537
2538#endif
2539
2540void RBConstruction::write_riesz_representors_to_files(const std::string & riesz_representors_dir,
2541 const bool write_binary_residual_representors)
2542{
2543 LOG_SCOPE("write_riesz_representors_to_files()", "RBConstruction");
2544
2545 // Write out Riesz representors. These are useful to have when restarting,
2546 // so you don't have to recompute them all over again.
2547
2548 // First we write out the Fq representors, these are independent of an RBEvaluation object.
2549 libMesh::out << "Writing out the Fq_representors..." << std::endl;
2550
2551 std::ostringstream file_name;
2552 const std::string riesz_representor_suffix = (write_binary_residual_representors ? ".xdr" : ".dat");
2553 struct stat stat_info;
2554
2555 // Residual representors written out to their own separate directory
2556 if ( this->processor_id() == 0)
2557 if ( Utility::mkdir(riesz_representors_dir.c_str()) != 0)
2558 libMesh::out << "Skipping creating residual_representors directory: " << strerror(errno) << std::endl;
2559
2560 for (auto i : index_range(Fq_representor))
2561 {
2562 if (Fq_representor[i])
2563 {
2564 file_name.str(""); // reset filename
2565 file_name << riesz_representors_dir << "/Fq_representor" << i << riesz_representor_suffix;
2566
2567 // Check to see if file exists, if so, don't overwrite it, we assume it was
2568 // there from a previous call to this function. Note: if stat returns zero
2569 // it means it successfully got the file attributes (and therefore the file
2570 // exists). Because of the following factors:
2571 // 1.) write_serialized_data takes longer for proc 0 than it does for others,
2572 // so processors can get out of sync.
2573 // 2.) The constructor for Xdr opens a (0 length) file on *all* processors,
2574 // there are typically hundreds of 0-length files created during this loop,
2575 // and that screws up checking for the existence of files. One way to stay
2576 // in sync is to but a barrier at each iteration of the loop -- not sure how
2577 // bad this will affect performance, but it can't be much worse than serialized
2578 // I/O already is :)
2579 int stat_result = stat(file_name.str().c_str(), &stat_info);
2580
2581 if ( (stat_result != 0) || // file definitely doesn't already exist
2582 (stat_info.st_size == 0)) // file exists, but has zero length (can happen if another proc already opened it!)
2583 {
2584 // No need to copy!
2585 // *solution = *(Fq_representor[i]);
2586 // std::swap doesn't work on pointers
2587 //std::swap(solution.get(), Fq_representor[i]);
2588 Fq_representor[i]->swap(*solution);
2589
2590 Xdr fqr_data(file_name.str(),
2591 write_binary_residual_representors ? ENCODE : WRITE);
2592
2593 write_serialized_data(fqr_data, false);
2594
2595 // Synchronize before moving on
2596 this->comm().barrier();
2597
2598 // Swap back.
2599 Fq_representor[i]->swap(*solution);
2600
2601 // TODO: bzip the resulting file? See $LIBMESH_DIR/src/mesh/unstructured_mesh.C
2602 // for the system call, be sure to do it only on one processor, etc.
2603 }
2604 }
2605 }
2606
2607
2608 // Next, write out the Aq representors associated with rb_eval.
2609 libMesh::out << "Writing out the Aq_representors..." << std::endl;
2610
2611 const unsigned int jstop = get_rb_evaluation().get_n_basis_functions();
2612 const unsigned int jstart = jstop-get_delta_N();
2613
2615 for (auto i : index_range(rbe.Aq_representor))
2616 for (unsigned int j=jstart; j<jstop; ++j)
2617 {
2618 libMesh::out << "Writing out Aq_representor[" << i << "][" << j << "]..." << std::endl;
2619 libmesh_assert(get_rb_evaluation().Aq_representor[i][j]);
2620
2621 file_name.str(""); // reset filename
2622 file_name << riesz_representors_dir
2623 << "/Aq_representor" << i << "_" << j << riesz_representor_suffix;
2624
2625 {
2626 // No need to copy! Use swap instead.
2627 // *solution = *(Aq_representor[i][j]);
2628 rbe.Aq_representor[i][j]->swap(*solution);
2629
2630 Xdr aqr_data(file_name.str(),
2631 write_binary_residual_representors ? ENCODE : WRITE);
2632
2633 write_serialized_data(aqr_data, false);
2634
2635 // Synchronize before moving on
2636 this->comm().barrier();
2637
2638 // Swap back.
2639 rbe.Aq_representor[i][j]->swap(*solution);
2640
2641 // TODO: bzip the resulting file? See $LIBMESH_DIR/src/mesh/unstructured_mesh.C
2642 // for the system call, be sure to do it only on one processor, etc.
2643 }
2644 }
2645}
2646
2647
2648
2649void RBConstruction::read_riesz_representors_from_files(const std::string & riesz_representors_dir,
2650 const bool read_binary_residual_representors)
2651{
2652 LOG_SCOPE("read_riesz_representors_from_files()", "RBConstruction");
2653
2654 libMesh::out << "Reading in the Fq_representors..." << std::endl;
2655
2656 const std::string riesz_representor_suffix = (read_binary_residual_representors ? ".xdr" : ".dat");
2657 std::ostringstream file_name;
2658 struct stat stat_info;
2659
2660 // Read in the Fq_representors. There should be Q_f of these. FIXME:
2661 // should we be worried about leaks here?
2662 for (const auto & rep : Fq_representor)
2663 libmesh_error_msg_if(rep, "Error, must delete existing Fq_representor before reading in from file.");
2664
2665 for (auto i : index_range(Fq_representor))
2666 {
2667 file_name.str(""); // reset filename
2668 file_name << riesz_representors_dir
2669 << "/Fq_representor" << i << riesz_representor_suffix;
2670
2671 // On processor zero check to be sure the file exists
2672 if (this->processor_id() == 0)
2673 {
2674 int stat_result = stat(file_name.str().c_str(), &stat_info);
2675
2676 libmesh_error_msg_if(stat_result != 0, "File does not exist: " << file_name.str());
2677 }
2678
2679 Xdr fqr_data(file_name.str(),
2680 read_binary_residual_representors ? DECODE : READ);
2681
2682 read_serialized_data(fqr_data, false);
2683
2685 Fq_representor[i]->init (this->n_dofs(), this->n_local_dofs(), false, PARALLEL);
2686
2687 // No need to copy, just swap
2688 // *Fq_representor[i] = *solution;
2689 Fq_representor[i]->swap(*solution);
2690 }
2691
2692 // Alert the update_residual_terms() function that we don't need to recompute
2693 // the Fq_representors as we have already read them in from file!
2695
2696
2697 libMesh::out << "Reading in the Aq_representors..." << std::endl;
2698
2699 // Read in the Aq representors. The class makes room for [Q_a][Nmax] of these. We are going to
2700 // read in [Q_a][get_rb_evaluation().get_n_basis_functions()]. FIXME:
2701 // should we be worried about leaks in the locations where we're about to fill entries?
2703 for (const auto & row : rbe.Aq_representor)
2704 for (const auto & rep : row)
2705 libmesh_error_msg_if(rep, "Error, must delete existing Aq_representor before reading in from file.");
2706
2707 // Now ready to read them in from file!
2708 for (auto i : index_range(rbe.Aq_representor))
2709 for (unsigned int j=0; j<rbe.get_n_basis_functions(); ++j)
2710 {
2711 file_name.str(""); // reset filename
2712 file_name << riesz_representors_dir
2713 << "/Aq_representor" << i << "_" << j << riesz_representor_suffix;
2714
2715 // On processor zero check to be sure the file exists
2716 if (this->processor_id() == 0)
2717 {
2718 int stat_result = stat(file_name.str().c_str(), &stat_info);
2719
2720 libmesh_error_msg_if(stat_result != 0, "File does not exist: " << file_name.str());
2721 }
2722
2723 Xdr aqr_data(file_name.str(), read_binary_residual_representors ? DECODE : READ);
2724
2725 read_serialized_data(aqr_data, false);
2726
2728 rbe.Aq_representor[i][j]->init (n_dofs(), n_local_dofs(),
2729 false, PARALLEL);
2730
2731 // No need to copy, just swap
2732 rbe.Aq_representor[i][j]->swap(*solution);
2733 }
2734}
2735
2737{
2739
2740 conv_flag = input_solver.get_converged_reason();
2741
2742 libmesh_error_msg_if(conv_flag < 0, "Convergence error. Error id: " << conv_flag);
2743}
2744
2749
2754
2759
2764
2769
2774
2775const std::vector<Number> &
2776RBConstruction::get_evaluated_thetas(unsigned int training_parameter_index) const
2777{
2779 libmesh_assert(training_parameter_index >= first_index);
2780
2781 const numeric_index_type local_index = training_parameter_index - first_index;
2782 libmesh_assert(local_index < _evaluated_thetas.size());
2783
2784 return _evaluated_thetas[local_index];
2785}
2786
2788{
2789 LOG_SCOPE("preevaluate_thetas()", "RBConstruction");
2790
2792
2793 // Early return if we've already preevaluated thetas.
2795 return;
2796
2797 if ( get_local_n_training_samples() == 0 )
2798 return;
2799
2800 auto & rb_theta_expansion = get_rb_evaluation().get_rb_theta_expansion();
2801 const unsigned int n_A_terms = rb_theta_expansion.get_n_A_terms();
2802 const unsigned int n_F_terms = rb_theta_expansion.get_n_F_terms();
2803 const unsigned int n_outputs = rb_theta_expansion.get_total_n_output_terms();
2804
2805 // Collect all training parameters
2806 // TODO: Here instead of using a vector of RBParameters objects,
2807 // we could use a single RBParameters object with multiple samples.
2808 // This would save memory over the current approach, but that may
2809 // not be a big deal in practice unless the number of training samples
2810 // is very large for some reason.
2811 std::vector<RBParameters> mus(get_local_n_training_samples());
2813 for (unsigned int i=0; i<get_local_n_training_samples(); i++)
2814 {
2815 // Load training parameter i, this is only loaded
2816 // locally since the RB solves are local.
2817 set_params_from_training_set( first_index+i );
2818 mus[i] = get_parameters();
2819 _evaluated_thetas[i].resize(n_A_terms + n_F_terms + n_outputs);
2820 }
2821
2822 // Evaluate thetas for all training parameters simultaneously
2823 for (unsigned int q_a=0; q_a<n_A_terms; q_a++)
2824 {
2825 const auto A_vals = rb_theta_expansion.eval_A_theta(q_a, mus);
2827 _evaluated_thetas[i][q_a] = A_vals[i];
2828 }
2829
2830 for (unsigned int q_f=0; q_f<n_F_terms; q_f++)
2831 {
2832 const auto F_vals = rb_theta_expansion.eval_F_theta(q_f, mus);
2834 _evaluated_thetas[i][n_A_terms + q_f] = F_vals[i];
2835 }
2836
2837 {
2838 unsigned int output_counter = 0;
2839 for (unsigned int n=0; n<rb_theta_expansion.get_n_outputs(); n++)
2840 for (unsigned int q_l=0; q_l<rb_theta_expansion.get_n_output_terms(n); q_l++)
2841 {
2842 // Evaluate the current output functional term for all
2843 // training parameters simultaneously.
2844 const auto output_vals = rb_theta_expansion.eval_output_theta(n, q_l, mus);
2845
2846 // TODO: the size of _evaluated_thetas is currently assumed to be
2847 // the same as get_local_n_training_samples(), but this won't be
2848 // the case if we use RBParameters objects that have multiple samples.
2849 // So just make sure that's the case for now.
2850 libmesh_error_msg_if(output_vals.size() != get_local_n_training_samples(),
2851 "We currently only support single-sample RBParameters "
2852 "objects during the training stage.");
2853
2854 for (auto i : index_range(output_vals))
2855 _evaluated_thetas[i][n_A_terms + n_F_terms + output_counter] = output_vals[i];
2856
2857 // Go to next output term
2858 output_counter++;
2859 }
2860 }
2861
2863}
2864
2869
2870} // namespace libMesh
unsigned int n_vars
void max(const T &r, T &o, Request &req) const
processor_id_type rank() const
This proxy class acts like a container of indices from a single coupling row.
This class defines a coupling matrix.
This class extends FEMContext in order to provide extra data required to perform local element residu...
const std::vector< dof_id_type > & get_neighbor_dof_indices() const
Accessor for neighbor dof indices.
virtual void side_fe_reinit() override
Override side_fe_reinit to set a boolean flag so that by default DG terms are assumed to be inactive.
const DenseMatrix< Number > & get_neighbor_neighbor_jacobian() const
Const accessor for element-neighbor Jacobian.
const DenseMatrix< Number > & get_elem_elem_jacobian() const
Const accessor for element-element Jacobian.
bool dg_terms_are_active() const
Are the DG terms active, i.e.
const DenseMatrix< Number > & get_neighbor_elem_jacobian() const
Const accessor for element-neighbor Jacobian.
const DenseMatrix< Number > & get_elem_neighbor_jacobian() const
Const accessor for element-neighbor Jacobian.
Defines a dense matrix for use in Finite Element-type computations.
void svd(DenseVector< Real > &sigma)
Compute the singular value decomposition of the matrix.
void get_transpose(DenseMatrix< T > &dest) const
Put the tranposed matrix into dest.
virtual T el(const unsigned int i, const unsigned int j) const override final
Defines a dense vector for use in Finite Element-type computations.
const std::vector< dof_id_type > & get_dof_indices() const
Accessor for element dof indices.
const DenseVector< Number > & get_elem_residual() const
Const accessor for element residual.
const DenseMatrix< Number > & get_elem_jacobian() const
Const accessor for element Jacobian.
dof_id_type first_dof(const processor_id_type proc) const
dof_id_type end_dof(const processor_id_type proc) const
This class handles the numbering of degrees of freedom on a mesh.
Definition dof_map.h:181
bool is_constrained_dof(const dof_id_type dof) const
Definition dof_map.h:2426
void attach_matrix(SparseMatrix< Number > &matrix)
Additional matrices may be attached to this DofMap.
Definition dof_map.C:240
void enforce_constraints_exactly(const System &system, NumericVector< Number > *v=nullptr, bool homogeneous=false) const
Constrains the numeric vector v, which represents a solution defined on the mesh.
Definition dof_map.h:2518
CouplingMatrix * _dof_coupling
Degree of freedom coupling.
Definition dof_map.h:1741
void constrain_element_matrix_and_vector(DenseMatrix< Number > &matrix, DenseVector< Number > &rhs, std::vector< dof_id_type > &elem_dofs, bool asymmetric_constraint_rows=true) const
Constrains the element matrix and vector.
Definition dof_map.h:2498
processor_id_type processor_id() const
Definition dof_object.h:881
ElemAssembly provides a per-element (interior and boundary) assembly functionality.
virtual void get_nodal_values(std::vector< dof_id_type > &, DenseMatrix< Number > &, DenseVector< Number > &, const System &, const Node &)
Get values to add to the matrix or rhs vector based on node.
virtual void interior_assembly(FEMContext &)
Perform the element interior assembly.
virtual void boundary_assembly(FEMContext &)
Perform the element boundary assembly.
virtual std::unique_ptr< Elem > side_ptr(unsigned int i)=0
virtual unsigned int n_sides() const =0
const Elem * neighbor_ptr(unsigned int i) const
Definition elem.h:2615
This is the EquationSystems class.
Parameters parameters
Data structure holding arbitrary parameters.
The ExodusII_IO class implements reading meshes in the ExodusII file format from Sandia National Labs...
Definition exodusII_io.h:53
virtual void write_equation_systems(const std::string &fname, const EquationSystems &es, const std::set< std::string > *system_names=nullptr) override
Writes out the solution for no specific time or timestep.
NumericVector< Number > * rhs
The system matrix.
virtual void attach_quadrature_rule(QBase *q)=0
Provides the class with the quadrature rule.
FEType get_fe_type() const
This class forms the foundation from which generic finite elements may be derived.
Definition fe_base.h:86
virtual void pre_fe_reinit(const System &, const Elem *e)
Reinitializes local data vectors/matrices on the current geometric element.
unsigned char side
Current side for side_* to examine.
const Elem & get_elem() const
Accessor for current Elem object.
virtual void elem_fe_reinit(const std::vector< Point > *const pts=nullptr)
Reinitializes interior FE objects on the current geometric element.
unsigned char get_side() const
Accessor for current side of Elem object.
void get_element_fe(unsigned int var, FEGenericBase< OutputShape > *&fe) const
Accessor for interior finite element object for variable var for the largest dimension in the mesh.
std::unique_ptr< QBase > default_quadrature_rule(const unsigned int dim, const int extraorder=0) const
Definition fe_type.C:34
SparseMatrix< Number > * matrix
The system matrix.
std::unique_ptr< LinearSolver< Number > > linear_solver
This class handles all the details of interfacing with various linear algebra packages like PETSc or ...
virtual LinearSolver< Number > * get_linear_solver() const override
unsigned int _n_linear_iterations
The number of linear iterations required to solve the linear system Ax=b.
Real _final_linear_residual
The final residual for the linear system Ax=b.
This base class can be inherited from to provide interfaces to linear solvers from different packages...
virtual LinearConvergenceReason get_converged_reason() const =0
virtual void init(const char *name=nullptr)=0
Initialize data structures if not done so already.
virtual void reuse_preconditioner(bool)
Set the same_preconditioner flag, which indicates if we reuse the same preconditioner for subsequent ...
virtual std::pair< unsigned int, Real > solve(SparseMatrix< T > &, NumericVector< T > &, NumericVector< T > &, const std::optional< double > tol=std::nullopt, const std::optional< unsigned int > m_its=std::nullopt)=0
This function calls the solver _solver_type preconditioned with the _preconditioner_type precondition...
This is the MeshBase class.
Definition mesh_base.h:81
A Node is like a Point, but with more information.
Definition node.h:55
Provides a uniform interface to vector storage schemes for different linear algebra libraries.
virtual void set(const numeric_index_type i, const T value)=0
Sets v(i) = value.
virtual void close()=0
Calls the NumericVector's internal assembly routines, ensuring that the values are consistent across ...
virtual T dot(const NumericVector< T > &v) const =0
virtual void print_matlab(const std::string &filename="") const
Print the contents of the vector in Matlab's sparse matrix format.
virtual void add_vector(const T *v, const std::vector< numeric_index_type > &dof_indices)
Computes , where v is a pointer and each dof_indices[i] specifies where to add value v[i].
static std::unique_ptr< NumericVector< T > > build(const Parallel::Communicator &comm, SolverPackage solver_package=libMesh::default_solver_package(), ParallelType parallel_type=AUTOMATIC)
Builds a NumericVector on the processors in communicator comm using the linear solver package specifi...
virtual void localize(std::vector< T > &v_local) const =0
Creates a copy of the global vector in the local vector v_local.
virtual Real l2_norm() const =0
virtual void scale(const T factor)=0
Scale each element of the vector by the given factor.
virtual void zero()=0
Set all entries to zero.
virtual void add(const numeric_index_type i, const T value)=0
Adds value to the vector entry specified by i.
processor_id_type processor_id() const
const Parallel::Communicator & comm() const
const T & get(std::string_view) const
Definition parameters.h:451
This class stores the set of ElemAssembly functor objects that define the "parameter-independent expa...
ElemAssembly & get_A_assembly(unsigned int q)
Return a reference to the specified A_assembly object.
ElemAssembly & get_output_assembly(unsigned int output_index, unsigned int q_l)
Return a reference to the specified output assembly object.
ElemAssembly & get_F_assembly(unsigned int q)
Return a reference to the specified F_assembly object.
void set_training_random_seed(int seed)
Set the seed that is used to randomly generate training parameters.
std::unique_ptr< NumericVector< Number > > inner_product_storage_vector
We keep an extra temporary vector that is useful for performing inner products (avoids unnecessary me...
virtual void clear()
Clear all the data structures associated with the system.
void set_quiet_mode(bool quiet_mode_in)
Set the quiet_mode flag.
bool quiet_mode
Flag to indicate whether we print out extra information during the Offline stage.
numeric_index_type get_first_local_training_index() const
Get the first local index of the training parameters.
numeric_index_type get_local_n_training_samples() const
Get the total number of training samples local to this processor.
void broadcast_parameters(const unsigned int proc_id)
Broadcasts parameters from processor proc_id to all processors.
numeric_index_type get_n_training_samples() const
Get the number of global training samples.
virtual void initialize_training_parameters(const RBParameters &mu_min, const RBParameters &mu_max, const unsigned int n_global_training_samples, const std::map< std::string, bool > &log_param_scale, const bool deterministic=true)
Initialize the parameter ranges and indicate whether deterministic or random training parameters shou...
void set_params_from_training_set(unsigned int global_index)
Set parameters to the RBParameters stored in index global_index of the global training set.
numeric_index_type get_last_local_training_index() const
Get the last local index of the training parameters.
bool serial_training_set
This boolean flag indicates whether or not the training set should be the same on all processors.
bool is_quiet() const
Is the system in quiet mode?
static void get_global_max_error_pair(const Parallel::Communicator &communicator, std::pair< numeric_index_type, Real > &error_pair)
Static function to return the error pair (index,error) that is corresponds to the largest error on al...
bool _normalize_solution_snapshots
Set this boolean to true if we want to normalize solution snapshots used in training to have norm of ...
virtual void load_training_set(const std::map< std::string, std::vector< RBParameter > > &new_training_set)
Overwrite the training parameters with new_training_set.
virtual bool is_serial_training_type(const std::string &RB_training_type_in)
std::vector< std::unique_ptr< SparseMatrix< Number > > > non_dirichlet_Aq_vector
We may also need a second set of matrices/vectors that do not have the Dirichlet boundary conditions ...
SparseMatrix< Number > * get_inner_product_matrix()
Get a pointer to inner_product_matrix.
bool _preevaluate_thetas_completed
Flag to indicate if the preevaluate_thetas function has been called, since this allows us to avoid ca...
ElemAssembly & get_inner_product_assembly()
virtual void write_riesz_representors_to_files(const std::string &riesz_representors_dir, const bool write_binary_residual_representors)
Write out all the Riesz representor data to files.
void enrich_basis_from_rhs_terms(const bool resize_rb_eval_data=true)
This function computes one basis function for each rhs term.
void set_convergence_assertion_flag(bool flag)
Setter for the flag determining if convergence should be checked after each solve.
void assemble_Fq_vector(unsigned int q, NumericVector< Number > *input_vector, bool apply_dof_constraints=true)
Assemble the q^th affine vector and store it in input_matrix.
bool get_convergence_assertion_flag() const
Getter for the flag determining if convergence should be checked after each solve.
virtual void allocate_data_structures()
Helper function that actually allocates all the data structures required by this class.
SparseMatrix< Number > * get_non_dirichlet_Aq_if_avail(unsigned int q)
Get a pointer to non_dirichlet_Aq if it's available, otherwise get Aq.
RBThetaExpansion & get_rb_theta_expansion()
Get a reference to the RBThetaExpansion object that that belongs to rb_eval.
std::unique_ptr< SparseMatrix< Number > > inner_product_matrix
The inner product matrix.
virtual void update_RB_system_matrices()
Compute the reduced basis matrices for the current basis.
std::vector< Number > Fq_representor_innerprods
Vectors storing the residual representor inner products to be used in computing the residuals online.
void print_basis_function_orthogonality() const
Print out a matrix that shows the orthogonality of the RB basis functions.
bool get_preevaluate_thetas_flag() const
Get/set flag to pre-evaluate the theta functions.
void zero_constrained_dofs_on_vector(NumericVector< Number > &vector) const
It is sometimes useful to be able to zero vector entries that correspond to constrained dofs.
virtual void set_Nmax(unsigned int Nmax)
virtual Real compute_max_error_bound()
(i) Compute the a posteriori error bound for each set of parameters in the training set,...
bool skip_residual_in_train_reduced_basis
Boolean flag to indicate if we skip residual calculations in train_reduced_basis.
unsigned int get_delta_N() const
Get delta_N, the number of basis functions we add to the RB space per iteration of the greedy algorit...
virtual void assemble_all_affine_operators()
Assemble and store all Q_a affine operators as well as the inner-product matrix.
void update_greedy_param_list()
Update the list of Greedily chosen parameters with current_parameters.
void set_rb_evaluation(RBEvaluation &rb_eval_in)
Set the RBEvaluation object.
const RBParameters & get_greedy_parameter(unsigned int i)
Return the parameters chosen during the i^th step of the Greedy algorithm.
NumericVector< Number > * get_output_vector(unsigned int n, unsigned int q_l)
Get a pointer to the n^th output.
virtual std::string system_type() const override
virtual bool greedy_termination_test(Real abs_greedy_error, Real initial_greedy_error, int count)
Function that indicates when to terminate the Greedy basis training.
virtual Real truth_solve(int plot_solution)
Perform a "truth" solve, i.e.
virtual void print_info() const
Print out info that describes the current setup of this RBConstruction.
void set_rb_construction_parameters(unsigned int n_training_samples_in, bool deterministic_training_in, int training_parameters_random_seed_in, bool quiet_mode_in, unsigned int Nmax_in, Real rel_training_tolerance_in, Real abs_training_tolerance_in, bool normalize_rb_error_bound_in_greedy_in, const std::string &RB_training_type_in, const RBParameters &mu_min_in, const RBParameters &mu_max_in, const std::map< std::string, std::vector< Real > > &discrete_parameter_values_in, const std::map< std::string, bool > &log_scaling, std::map< std::string, std::vector< RBParameter > > *training_sample_list=nullptr)
Set the state of this RBConstruction object based on the arguments to this function.
void add_scaled_Aq(Number scalar, unsigned int q_a, SparseMatrix< Number > *input_matrix, bool symmetrize)
Add the scaled q^th affine matrix to input_matrix.
virtual void compute_Fq_representor_innerprods(bool compute_inner_products=true)
Compute the terms that are combined ‘online’ to determine the dual norm of the residual.
virtual void set_context_solution_vec(NumericVector< Number > &vec)
Set current_local_solution = vec so that we can access vec from FEMContext during assembly.
virtual std::unique_ptr< DGFEMContext > build_context()
Builds a DGFEMContext object with enough information to do evaluations on each element.
RBConstruction(EquationSystems &es, const std::string &name, const unsigned int number)
Constructor.
virtual void update_system()
Update the system after enriching the RB space; this calls a series of functions to update the system...
bool store_dirichlet_operators
Boolean flag to indicate whether we store affine operator matrices and vectors with constraints enfor...
Real get_rel_training_tolerance() const
virtual void assemble_misc_matrices()
Assemble and store all the inner-product matrix, the constraint matrix (for constrained problems) and...
virtual void assemble_affine_expansion(bool skip_matrix_assembly, bool skip_vector_assembly)
Assemble the matrices and vectors for this system.
NumericVector< Number > * get_non_dirichlet_Fq(unsigned int q)
Get a pointer to non-Dirichlet Fq.
void check_convergence(LinearSolver< Number > &input_solver)
Check if the linear solver reports convergence.
bool use_empty_rb_solve_in_greedy
A boolean flag to indicate whether or not we initialize the Greedy algorithm by performing rb_solves ...
std::vector< std::unique_ptr< NumericVector< Number > > > Fq_vector
Vector storing the Q_f vectors in the affine decomposition of the right-hand side.
std::string RB_training_type
This string indicates the type of training that we will use.
virtual void post_process_truth_solution()
Similarly, provide an opportunity to post-process the truth solution after the solve is complete.
RBEvaluation * rb_eval
The current RBEvaluation object we are using to perform the Evaluation stage of the reduced basis met...
bool compute_RB_inner_product
Boolean flag to indicate whether we compute the RB_inner_product_matrix.
const std::vector< Number > & get_evaluated_thetas(unsigned int training_parameter_index) const
Return the evaluated theta functions at the given training parameter index.
RBEvaluation & get_rb_evaluation()
Get a reference to the RBEvaluation object.
void train_reduced_basis_with_POD()
Train the reduced basis using Proper Orthogonal Decomposition (POD).
virtual void preevaluate_thetas()
bool normalize_rb_bound_in_greedy
This boolean indicates if we normalize the RB error in the greedy using RBEvaluation::get_error_bound...
virtual Real get_RB_error_bound()
std::vector< Number > energy_inner_product_coeffs
We may optionally want to use the "energy inner-product" rather than the inner-product assembly speci...
std::vector< std::unique_ptr< SparseMatrix< Number > > > Aq_vector
Vector storing the Q_a matrices from the affine expansion.
virtual void init_context(FEMContext &)
Initialize the FEMContext prior to performing an element loop.
virtual void compute_output_dual_innerprods()
Compute and store the dual norm of each output functional.
bool impose_internal_fluxes
Boolean flag to indicate whether we impose "fluxes" (i.e.
NumericVector< Number > * get_non_dirichlet_Fq_if_avail(unsigned int q)
Get a pointer to non_dirichlet_Fq if it's available, otherwise get Fq.
Real train_reduced_basis_with_greedy(const bool resize_rb_eval_data)
Train the reduced basis using the "Greedy algorithm.".
SparseMatrix< Number > * get_non_dirichlet_Aq(unsigned int q)
Get a pointer to non_dirichlet_Aq.
Real get_abs_training_tolerance() const
void set_inner_product_assembly(ElemAssembly &inner_product_assembly_in)
Set the rb_assembly_expansion object.
std::vector< std::vector< Number > > output_dual_innerprods
The vector storing the dual norm inner product terms for each output.
bool skip_degenerate_sides
In some cases meshes are intentionally created with degenerate sides as a way to represent,...
virtual void initialize_rb_construction(bool skip_matrix_assembly=false, bool skip_vector_assembly=false)
Allocate all the data structures necessary for the construction stage of the RB method.
bool exit_on_repeated_greedy_parameters
Boolean flag to indicate whether we exit the greedy if we select the same parameters twice in a row.
SparseMatrix< Number > * get_non_dirichlet_inner_product_matrix_if_avail()
Get the non-Dirichlet inner-product matrix if it's available, otherwise get the inner-product matrix ...
std::unique_ptr< NumericVector< Number > > _untransformed_solution
We also store a copy of the untransformed solution in order to create _untransformed_basis_functions.
bool output_dual_innerprods_computed
A boolean flag to indicate whether or not the output dual norms have already been computed — used to ...
void set_rb_assembly_expansion(RBAssemblyExpansion &rb_assembly_expansion_in)
Set the rb_assembly_expansion object.
std::vector< std::vector< std::unique_ptr< NumericVector< Number > > > > outputs_vector
The libMesh vectors that define the output functionals.
void set_current_training_parameter_index(unsigned int index)
NumericVector< Number > * get_Fq(unsigned int q)
Get a pointer to Fq.
virtual Real train_reduced_basis(const bool resize_rb_eval_data=true)
Train the reduced basis.
void add_scaled_matrix_and_vector(Number scalar, ElemAssembly *elem_assembly, SparseMatrix< Number > *input_matrix, NumericVector< Number > *input_vector, bool symmetrize=false, bool apply_dof_constraints=true)
This function loops over the mesh and applies the specified interior and/or boundary assembly routine...
virtual void load_basis_function(unsigned int i)
Load the i^th RB function into the RBConstruction solution vector.
void set_energy_inner_product(const std::vector< Number > &energy_inner_product_coeffs_in)
Specify the coefficients of the A_q operators to be used in the energy inner-product.
virtual void solve_for_matrix_and_rhs(LinearSolver< Number > &input_solver, SparseMatrix< Number > &input_matrix, NumericVector< Number > &input_rhs)
Assembles & solves the linear system A*x=b for the specified matrix input_matrix and right-hand side ...
virtual bool check_if_zero_truth_solve() const
void assemble_Aq_matrix(unsigned int q, SparseMatrix< Number > *input_matrix, bool apply_dof_constraints=true)
Assemble the q^th affine matrix and store it in input_matrix.
RBAssemblyExpansion & get_rb_assembly_expansion()
std::unique_ptr< LinearSolver< Number > > inner_product_solver
We store an extra linear solver object which we can optionally use for solving all systems in which t...
void set_normalize_rb_bound_in_greedy(bool normalize_rb_bound_in_greedy_in)
Get/set the boolean to indicate if we normalize the RB error in the greedy.
LinearSolver< Number > * extra_linear_solver
Also, we store a pointer to an extra linear solver.
unsigned int get_Nmax() const
Get/set Nmax, the maximum number of RB functions we are willing to compute.
std::vector< Real > training_error_bounds
Vector storing the values of the error bound for each parameter in the training set — the parameter g...
void set_abs_training_tolerance(Real new_training_tolerance)
Get/set the absolute tolerance for the basis training.
void reset_preevaluate_thetas_completed()
Reset the _preevaluate_thetas_completed flag to false.
void set_rel_training_tolerance(Real new_training_tolerance)
Get/set the relative tolerance for the basis training.
std::vector< Number > truth_outputs
Vector storing the truth output values from the most recent truth solve.
std::vector< std::vector< Number > > _evaluated_thetas
Storage of evaluated theta functions at a set of parameters.
virtual void get_output_vectors(std::map< std::string, NumericVector< Number > * > &all_vectors)
Get a map that stores pointers to all of the vectors.
virtual void assemble_all_affine_vectors()
Assemble and store the affine RHS vectors.
Real rel_training_tolerance
Relative and absolute tolerances for training reduced basis using the Greedy scheme.
bool use_energy_inner_product
Boolean to indicate whether we're using the energy inner-product.
virtual SparseMatrix< Number > & get_matrix_for_output_dual_solves()
Return the matrix for the output residual dual norm solves.
virtual void load_rb_solution()
Load the RB solution from the most recent solve with rb_eval into this system's solution vector.
virtual void get_all_vectors(std::map< std::string, NumericVector< Number > * > &all_vectors)
Get a map that stores pointers to all of the vectors.
virtual void enrich_RB_space()
Add a new basis function to the RB space.
const std::string & get_RB_training_type() const
bool store_untransformed_basis
Boolean flag to indicate whether we store a second copy of the basis without constraints or dof trans...
void set_RB_training_type(const std::string &RB_training_type_in)
Get/set the string that determines the training type.
virtual void read_riesz_representors_from_files(const std::string &riesz_representors_dir, const bool write_binary_residual_representors)
Read in all the Riesz representor data from files.
virtual void assemble_all_output_vectors()
Assemble and store the output vectors.
void assemble_inner_product_matrix(SparseMatrix< Number > *input_matrix, bool apply_dof_constraints=true)
Assemble the inner product matrix and store it in input_matrix.
bool Fq_representor_innerprods_computed
A boolean flag to indicate whether or not the Fq representor norms have already been computed — used ...
std::vector< std::unique_ptr< NumericVector< Number > > > Fq_representor
Vector storing the residual representors associated with the right-hand side.
SparseMatrix< Number > * get_non_dirichlet_inner_product_matrix()
Get the non-Dirichlet (or more generally no-constraints) version of the inner-product matrix.
std::unique_ptr< SparseMatrix< Number > > non_dirichlet_inner_product_matrix
std::vector< std::unique_ptr< NumericVector< Number > > > non_dirichlet_Fq_vector
RBAssemblyExpansion * rb_assembly_expansion
This member holds the (parameter independent) assembly functors that define the "affine expansion" of...
virtual void update_residual_terms(bool compute_inner_products=true)
Compute the terms that are combined ‘online’ to determine the dual norm of the residual.
std::vector< std::unique_ptr< NumericVector< Number > > > _untransformed_basis_functions
In cases where we have dof transformations such as a change of coordinates at some nodes we need to s...
void set_preevaluate_thetas_flag(bool flag)
virtual void truth_assembly()
Assemble the truth matrix and right-hand side for current_parameters.
virtual void post_process_elem_matrix_and_vector(DGFEMContext &)
This function is called from add_scaled_matrix_and_vector() before each element matrix and vector are...
bool get_normalize_rb_bound_in_greedy() const
unsigned int Nmax
Maximum number of reduced basis functions we are willing to use.
std::vector< std::vector< std::unique_ptr< NumericVector< Number > > > > non_dirichlet_outputs_vector
Real compute_residual_dual_norm_slow(const unsigned int N)
The slow (but simple, non-error prone) way to compute the residual dual norm.
bool store_non_dirichlet_operators
Boolean flag to indicate whether we store a second copy of each affine operator and vector which does...
bool _preevaluate_thetas_flag
Flag to indicate if we preevaluate the theta functions.
virtual void get_all_matrices(std::map< std::string, SparseMatrix< Number > * > &all_matrices)
Get a map that stores pointers to all of the matrices.
virtual void recompute_all_residual_terms(const bool compute_inner_products=true)
This function computes all of the residual representors, can be useful when restarting a basis traini...
bool is_rb_eval_initialized() const
unsigned int _current_training_parameter_index
The current training parameter index during reduced basis training.
virtual void process_parameters_file(const std::string &parameters_filename)
Read in from the file specified by parameters_filename and set the this system's member variables acc...
SparseMatrix< Number > * get_Aq(unsigned int q)
Get a pointer to Aq.
unsigned int delta_N
The number of basis functions that we add at each greedy step.
NumericVector< Number > * get_non_dirichlet_output_vector(unsigned int n, unsigned int q_l)
Get a pointer to non-Dirichlet output vector.
static std::unique_ptr< DirichletBoundary > build_zero_dirichlet_boundary_object()
It's helpful to be able to generate a DirichletBoundary that stores a ZeroFunction in order to impose...
virtual void clear() override
Clear all the data structures associated with the system.
ElemAssembly * inner_product_assembly
Pointer to inner product assembly.
unsigned int get_current_training_parameter_index() const
Get/set the current training parameter index.
bool assert_convergence
A boolean flag to indicate whether to check for proper convergence after each solve.
This class is part of the rbOOmit framework.
NumericVector< Number > & get_basis_function(unsigned int i)
Get a reference to the i^th basis function.
DenseMatrix< Number > RB_inner_product_matrix
The inner product matrix.
std::vector< Number > Fq_representor_innerprods
Vectors storing the residual representor inner products to be used in computing the residuals online.
std::vector< std::unique_ptr< NumericVector< Number > > > basis_functions
The libMesh vectors storing the finite element coefficients of the RB basis functions.
virtual void resize_data_structures(const unsigned int Nmax, bool resize_error_bound_data=true)
Resize and clear the data vectors corresponding to the value of Nmax.
virtual Real rb_solve(unsigned int N)
Perform online solve with the N RB basis functions, for the set of parameters in current_params,...
std::vector< std::vector< std::unique_ptr< NumericVector< Number > > > > Aq_representor
Vector storing the residual representors associated with the left-hand side.
virtual Real get_error_bound_normalization()
std::vector< std::vector< std::vector< Number > > > Aq_Aq_representor_innerprods
virtual unsigned int get_n_basis_functions() const
Get the current number of basis functions.
std::vector< std::vector< std::vector< Number > > > Fq_Aq_representor_innerprods
Vectors storing the residual representor inner products to be used in computing the residuals online.
std::vector< std::vector< DenseVector< Number > > > RB_output_vectors
The vectors storing the RB output vectors.
RBThetaExpansion & get_rb_theta_expansion()
Get a reference to the rb_theta_expansion.
std::vector< RBParameters > greedy_param_list
The list of parameters selected by the Greedy algorithm in generating the Reduced Basis associated wi...
std::vector< std::vector< Number > > output_dual_innerprods
The vector storing the dual norm inner product terms for each output.
std::vector< DenseVector< Number > > RB_Fq_vector
Dense vector for the RHS.
DenseVector< Number > RB_solution
The RB solution vector.
std::vector< DenseMatrix< Number > > RB_Aq_vector
Dense matrices for the RB computations.
This class is part of the rbOOmit framework.
void set_value(const std::string &param_name, Real value)
Set the value of the specified parameter.
const RBParameters & get_parameters_max() const
Get an RBParameters object that specifies the maximum allowable value for each parameter.
const RBParameters & get_parameters() const
Get the current parameters.
unsigned int get_n_params() const
Get the number of parameters.
bool set_parameters(const RBParameters &params)
Set the current parameters to params The parameters are checked for validity; an error is thrown if t...
void initialize_parameters(const RBParameters &mu_min_in, const RBParameters &mu_max_in, const std::map< std::string, std::vector< Real > > &discrete_parameter_values)
Initialize the parameter ranges and set current_parameters.
Real get_parameter_max(const std::string &param_name) const
Get maximum allowable value of parameter param_name.
void print_discrete_parameter_values() const
Print out all the discrete parameter values.
const RBParameters & get_parameters_min() const
Get an RBParameters object that specifies the minimum allowable value for each parameter.
Real get_parameter_min(const std::string &param_name) const
Get minimum allowable value of parameter param_name.
void print_parameters() const
Print the current parameters.
bool is_discrete_parameter(const std::string &mu_name) const
Is parameter mu_name discrete?
This class stores the set of RBTheta functor objects that define the "parameter-dependent expansion" ...
unsigned int get_n_F_terms() const
Get Q_f, the number of terms in the affine expansion for the right-hand side.
unsigned int get_n_A_terms() const
Get Q_a, the number of terms in the affine expansion for the bilinear form.
virtual Number eval_A_theta(unsigned int q, const RBParameters &mu) const
Evaluate theta_q_a at the current parameter.
unsigned int get_n_output_terms(unsigned int output_index) const
Get the number of affine terms associated with the specified output.
unsigned int get_n_outputs() const
Get n_outputs, the number output functionals.
virtual Number eval_output_theta(unsigned int output_index, unsigned int q_l, const RBParameters &mu) const
Evaluate theta_q_l at the current parameter.
Generic sparse matrix.
virtual void close()=0
Calls the SparseMatrix's internal assembly routines, ensuring that the values are consistent across p...
static std::unique_ptr< SparseMatrix< T > > build(const Parallel::Communicator &comm, const SolverPackage solver_package=libMesh::default_solver_package(), const MatrixBuildType matrix_build_type=MatrixBuildType::AUTOMATIC)
Builds a SparseMatrix<T> using the linear solver package specified by solver_package.
void vector_mult(NumericVector< T > &dest, const NumericVector< T > &arg) const
Multiplies the matrix by the NumericVector arg and stores the result in NumericVector dest.
virtual void add_matrix(const DenseMatrix< T > &dm, const std::vector< numeric_index_type > &rows, const std::vector< numeric_index_type > &cols)=0
Add the full matrix dm to the SparseMatrix.
virtual void zero()=0
Set all entries to 0.
virtual void add(const numeric_index_type i, const numeric_index_type j, const T value)=0
Add value to the element (i,j).
const std::string & name() const
Definition system.h:2385
std::unique_ptr< NumericVector< Number > > current_local_solution
All the values I need to compute my contribution to the simulation at hand.
Definition system.h:1667
void read_serialized_data(Xdr &io, const bool read_additional_data=true)
Reads additional data, namely vectors, for this System.
Definition system_io.C:533
dof_id_type n_dofs() const
Definition system.C:118
bool assemble_before_solve
Flag which tells the system to whether or not to call the user assembly function during each call to ...
Definition system.h:1609
dof_id_type n_local_dofs() const
Definition system.C:155
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
Definition system.h:1655
virtual void update()
Update the local values to reflect the solution on neighboring processors.
Definition system.C:498
const DofMap & get_dof_map() const
Definition system.h:2417
const EquationSystems & get_equation_systems() const
Definition system.h:767
const MeshBase & get_mesh() const
Definition system.h:2401
void write_serialized_data(Xdr &io, const bool write_additional_data=true) const
Writes additional data, namely vectors, for this System.
Definition system_io.C:1518
The Tri3Subdivision element is a three-noded subdivision surface shell element used in mechanics calc...
This class implements a C++ interface to the XDR (eXternal Data Representation) format.
Definition xdr_cxx.h:68
ConstFunction that simply returns 0.
MeshBase & mesh
std::string get_timestamp()
Definition timestamp.C:37
int mkdir(const char *pathname)
Create a directory.
Definition utility.C:152
The libMesh namespace provides an interface to certain functionality in the library.
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition int_range.h:153
ElemType
Defines an enum for geometric element types.
void libmesh_ignore(const Args &...)
T libmesh_real(T a)
libmesh_assert(ctx)
OStreamProxy out
dof_id_type numeric_index_type
Definition id_types.h:99
uint8_t dof_id_type
Definition id_types.h:67
T libmesh_conj(T a)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition int_range.h:176
LinearConvergenceReason
Linear solver convergence flags (taken from the PETSc flags).
boost::multiprecision::float128 real(const boost::multiprecision::float128 in)
static const bool value
Definition xdr_io.C:55