libMesh
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 
63 namespace 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.
99  assemble_before_solve = false;
100 }
101 
103 
105 {
106  LOG_SCOPE("clear()", "RBConstruction");
107 
108  Parent::clear();
109 
111  {
112  Aq_vector.clear();
113  Fq_vector.clear();
114  outputs_vector.clear();
115  }
116 
118  {
119  non_dirichlet_Aq_vector.clear();
120  non_dirichlet_Fq_vector.clear();
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 
132 std::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 
199 {
201 }
202 
204 {
206 }
207 
208 void 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 {
423  use_energy_inner_product = false;
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 
435  return *inner_product_assembly;
436 }
437 
438 void 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 
466 void 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 
496 void 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
502  this->assemble_misc_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.
527  unsigned int Q_f_hat = get_rb_theta_expansion().get_n_F_terms()*(get_rb_theta_expansion().get_n_F_terms()+1)/2;
528  Fq_representor_innerprods.resize(Q_f_hat);
529 
530  // Resize the output vectors
531  outputs_vector.resize(get_rb_theta_expansion().get_n_outputs());
532  for (unsigned int n=0; n<get_rb_theta_expansion().get_n_outputs(); n++)
534 
535  // Resize the output dual norm vectors
536  output_dual_innerprods.resize(get_rb_theta_expansion().get_n_outputs());
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  {
619  non_dirichlet_outputs_vector.resize(get_rb_theta_expansion().get_n_outputs());
620  for (unsigned int n=0; n<get_rb_theta_expansion().get_n_outputs(); n++)
621  {
622  non_dirichlet_outputs_vector[n].resize( get_rb_theta_expansion().get_n_output_terms(n) );
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 
636 std::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  {
989  matrix->add(get_rb_theta_expansion().eval_A_theta(q_a, mu), *get_Aq(q_a));
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 
1193 Real 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 
1218  RBEvaluation & rbe = get_rb_evaluation();
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;
1288  print_parameters();
1289 
1290  // Update the list of Greedily selected parameters
1291  this->update_greedy_param_list();
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;
1304  enrich_RB_space();
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  }
1326  this->update_greedy_param_list();
1327 
1328  return training_greedy_error;
1329 }
1330 
1331 void 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 
1346  truth_assembly();
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
1361  if (extra_linear_solver)
1362  {
1363  // If extra_linear_solver has been initialized, then we use it for the
1364  // truth solves.
1366 
1367  if (assert_convergence)
1369  }
1370  else
1371  {
1373 
1374  if (assert_convergence)
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;
1419  enrich_RB_space();
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 
1582  RBEvaluation & rbe = get_rb_evaluation();
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 
1603 {
1605 }
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 
1624  truth_assembly();
1625 
1626  // truth_assembly assembles into matrix and rhs, so use those for the solve
1627  if (extra_linear_solver)
1628  {
1629  // If extra_linear_solver has been initialized, then we use it for the
1630  // truth solves.
1632 
1633  if (assert_convergence)
1635  }
1636  else
1637  {
1639 
1640  if (assert_convergence)
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 
1681 bool RBConstruction::is_serial_training_type(const std::string & RB_training_type_in)
1682 {
1683  return (RB_training_type_in == "POD");
1684 }
1685 
1686 void 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 
1698 const std::string & RBConstruction::get_RB_training_type() const
1699 {
1700  return RB_training_type;
1701 }
1702 
1703 void 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 
1818 void 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.
1871  set_current_training_parameter_index(first_index+i);
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
1888  if (serial_training_set)
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  {
1923  get_rb_evaluation().RB_Fq_vector[q_f](i) = get_non_dirichlet_Fq_if_avail(q_f)->dot(get_rb_evaluation().get_basis_function(i));
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  {
1932  get_rb_evaluation().RB_output_vectors[n][q_l](i) =
1933  get_output_vector(n,q_l)->dot(get_rb_evaluation().get_basis_function(i));
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 
1979 void 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 
2025  if (assert_convergence)
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 
2087 {
2088  return *inner_product_matrix;
2089 }
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 
2141  if (assert_convergence)
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 
2186 void RBConstruction::compute_Fq_representor_innerprods(bool compute_inner_products)
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 
2216  if (assert_convergence)
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()))
2268  solution->add(get_rb_evaluation().RB_solution(i), get_rb_evaluation().get_basis_function(i));
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 
2352 {
2354  {
2356  }
2357 
2358  return get_inner_product_matrix();
2359 }
2360 
2362 {
2364  {
2366  }
2367 
2368  return get_inner_product_matrix();
2369 }
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 
2455 void 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 
2481 void 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 
2503 void 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 
2527 std::unique_ptr<DirichletBoundary> RBConstruction::build_zero_dirichlet_boundary_object()
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 
2540 void 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 
2614  RBEvaluation & rbe = get_rb_evaluation();
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 
2649 void 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?
2702  RBEvaluation & rbe = get_rb_evaluation();
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 
2727  rbe.Aq_representor[i][j] = NumericVector<Number>::build(this->comm());
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 
2746 {
2747  return assert_convergence;
2748 }
2749 
2751 {
2752  assert_convergence = flag;
2753 }
2754 
2756 {
2757  return _preevaluate_thetas_flag;
2758 }
2759 
2761 {
2762  _preevaluate_thetas_flag = flag;
2763 }
2764 
2766 {
2768 }
2769 
2771 {
2773 }
2774 
2775 const std::vector<Number> &
2776 RBConstruction::get_evaluated_thetas(unsigned int training_parameter_index) const
2777 {
2778  const numeric_index_type first_index = get_first_local_training_index();
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());
2812  const numeric_index_type first_index = get_first_local_training_index();
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);
2826  for (auto i : make_range(get_local_n_training_samples()))
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);
2833  for (auto i : make_range(get_local_n_training_samples()))
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 
2866 {
2868 }
2869 
2870 } // namespace libMesh
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.
T libmesh_real(T a)
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_dirichlet_operators
Boolean flag to indicate whether we store affine operator matrices and vectors with constraints enfor...
numeric_index_type get_first_local_training_index() const
Get the first local index of the training parameters.
bool normalize_rb_bound_in_greedy
This boolean indicates if we normalize the RB error in the greedy using RBEvaluation::get_error_bound...
std::vector< std::vector< std::unique_ptr< NumericVector< Number > > > > Aq_representor
Vector storing the residual representors associated with the left-hand side.
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.
ElemType
Defines an enum for geometric element types.
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:1521
dof_id_type end_dof(const processor_id_type proc) const
Definition: dof_map_base.h:191
This is the EquationSystems class.
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.
const DenseMatrix< Number > & get_elem_jacobian() const
Const accessor for element Jacobian.
Definition: diff_context.h:274
std::vector< Real > training_error_bounds
Vector storing the values of the error bound for each parameter in the training set — the parameter ...
bool get_normalize_rb_bound_in_greedy() const
bool quiet_mode
Flag to indicate whether we print out extra information during the Offline stage. ...
A Node is like a Point, but with more information.
Definition: node.h:52
virtual void truth_assembly()
Assemble the truth matrix and right-hand side for current_parameters.
void set_RB_training_type(const std::string &RB_training_type_in)
Get/set the string that determines the training type.
std::vector< Number > Fq_representor_innerprods
Vectors storing the residual representor inner products to be used in computing the residuals online...
virtual void pre_fe_reinit(const System &, const Elem *e)
Reinitializes local data vectors/matrices on the current geometric element.
Definition: fem_context.C:1683
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...
boost::multiprecision::float128 real(const boost::multiprecision::float128 in)
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.
std::vector< std::vector< Number > > _evaluated_thetas
Storage of evaluated theta functions at a set of parameters.
std::vector< std::unique_ptr< NumericVector< Number > > > Fq_representor
Vector storing the residual representors associated with the right-hand side.
T libmesh_conj(T a)
ElemAssembly & get_F_assembly(unsigned int q)
Return a reference to the specified F_assembly object.
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...
unsigned int get_n_F_terms() const
Get Q_f, the number of terms in the affine expansion for the right-hand side.
bool assert_convergence
A boolean flag to indicate whether to check for proper convergence after each solve.
ConstFunction that simply returns 0.
Definition: zero_function.h:38
virtual Real train_reduced_basis(const bool resize_rb_eval_data=true)
Train the reduced basis.
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.
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 get_all_matrices(std::map< std::string, SparseMatrix< Number > *> &all_matrices)
Get a map that stores pointers to all of the matrices.
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 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
DenseVector< Number > RB_solution
The RB solution vector.
void set_preevaluate_thetas_flag(bool flag)
virtual Real truth_solve(int plot_solution)
Perform a "truth" solve, i.e.
RBConstruction(EquationSystems &es, const std::string &name, const unsigned int number)
Constructor.
unsigned int _n_linear_iterations
The number of linear iterations required to solve the linear system Ax=b.
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...
const Elem & get_elem() const
Accessor for current Elem object.
Definition: fem_context.h:908
std::string get_timestamp()
Definition: timestamp.C:37
The ExodusII_IO class implements reading meshes in the ExodusII file format from Sandia National Labs...
Definition: exodusII_io.h:50
Real get_parameter_min(const std::string &param_name) const
Get minimum allowable value of parameter param_name.
DenseMatrix< Number > RB_inner_product_matrix
The inner product matrix.
Real get_parameter_max(const std::string &param_name) const
Get maximum allowable value of parameter param_name.
SparseMatrix< Number > * get_Aq(unsigned int q)
Get a pointer to Aq.
bool compute_RB_inner_product
Boolean flag to indicate whether we compute the RB_inner_product_matrix.
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...
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 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]...
const std::vector< dof_id_type > & get_neighbor_dof_indices() const
Accessor for neighbor dof indices.
virtual void interior_assembly(FEMContext &)
Perform the element interior assembly.
Definition: elem_assembly.h:55
std::vector< std::unique_ptr< NumericVector< Number > > > basis_functions
The libMesh vectors storing the finite element coefficients of the RB basis functions.
const RBParameters & get_parameters_max() const
Get an RBParameters object that specifies the maximum allowable value for each parameter.
const EquationSystems & get_equation_systems() const
Definition: system.h:767
void sum(T &r) const
void attach_matrix(SparseMatrix< Number > &matrix)
Additional matrices may be attached to this DofMap.
Definition: dof_map.C:240
void enrich_basis_from_rhs_terms(const bool resize_rb_eval_data=true)
This function computes one basis function for each rhs term.
std::vector< std::vector< Number > > output_dual_innerprods
The vector storing the dual norm inner product terms for each output.
void barrier() const
MeshBase & mesh
ElemAssembly & get_inner_product_assembly()
void set_quiet_mode(bool quiet_mode_in)
Set the quiet_mode flag.
processor_id_type rank() const
NumericVector< Number > * rhs
The system matrix.
LinearConvergenceReason
Linear solver convergence flags (taken from the PETSc flags).
unsigned int get_n_A_terms() const
Get Q_a, the number of terms in the affine expansion for the bilinear form.
bool get_convergence_assertion_flag() const
Getter for the flag determining if convergence should be checked after each solve.
This proxy class acts like a container of indices from a single coupling row.
const Parallel::Communicator & comm() const
bool is_quiet() const
Is the system in quiet mode?
bool skip_degenerate_sides
In some cases meshes are intentionally created with degenerate sides as a way to represent, say, triangles using a hex-only mesh.
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.
unsigned int m() const
ElemAssembly & get_output_assembly(unsigned int output_index, unsigned int q_l)
Return a reference to the specified output assembly object.
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.
const RBParameters & get_greedy_parameter(unsigned int i)
Return the parameters chosen during the i^th step of the Greedy algorithm.
int mkdir(const char *pathname)
Create a directory.
Definition: utility.C:152
std::vector< std::vector< Number > > output_dual_innerprods
The vector storing the dual norm inner product terms for each output.
std::string RB_training_type
This string indicates the type of training that we will use.
SparseMatrix< Number > * get_non_dirichlet_inner_product_matrix()
Get the non-Dirichlet (or more generally no-constraints) version of the inner-product matrix...
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...
NumericVector< Number > * get_non_dirichlet_Fq_if_avail(unsigned int q)
Get a pointer to non_dirichlet_Fq if it&#39;s available, otherwise get Fq.
virtual std::unique_ptr< DGFEMContext > build_context()
Builds a DGFEMContext object with enough information to do evaluations on each element.
virtual LinearSolver< Number > * get_linear_solver() const override
std::unique_ptr< SparseMatrix< Number > > inner_product_matrix
The inner product matrix.
virtual Real get_error_bound_normalization()
const DenseMatrix< Number > & get_neighbor_neighbor_jacobian() const
Const accessor for element-neighbor Jacobian.
const std::string & get_RB_training_type() const
The libMesh namespace provides an interface to certain functionality in the library.
NumericVector< Number > * get_Fq(unsigned int q)
Get a pointer to Fq.
virtual T dot(const NumericVector< T > &v) const =0
dof_id_type n_local_dofs() const
Definition: system.C:155
std::vector< Number > Fq_representor_innerprods
Vectors storing the residual representor inner products to be used in computing the residuals online...
std::vector< RBParameters > greedy_param_list
The list of parameters selected by the Greedy algorithm in generating the Reduced Basis associated wi...
unsigned int get_current_training_parameter_index() const
Get/set the current training parameter index.
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.
Definition: elem_assembly.h:67
unsigned int get_n_outputs() const
Get n_outputs, the number output functionals.
Real get_rel_training_tolerance() const
unsigned char get_side() const
Accessor for current side of Elem object.
Definition: fem_context.h:922
const MeshBase & get_mesh() const
Definition: system.h:2401
void broadcast_parameters(const unsigned int proc_id)
Broadcasts parameters from processor proc_id to all processors.
void reset_preevaluate_thetas_completed()
Reset the _preevaluate_thetas_completed flag to false.
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.
virtual void print_info() const
Print out info that describes the current setup of this RBConstruction.
This class stores the set of RBTheta functor objects that define the "parameter-dependent expansion" ...
static std::unique_ptr< DirichletBoundary > build_zero_dirichlet_boundary_object()
It&#39;s helpful to be able to generate a DirichletBoundary that stores a ZeroFunction in order to impose...
std::vector< Number > energy_inner_product_coeffs
We may optionally want to use the "energy inner-product" rather than the inner-product assembly speci...
dof_id_type n_dofs() const
Definition: system.C:118
virtual void init_context(FEMContext &)
Initialize the FEMContext prior to performing an element loop.
virtual Real compute_max_error_bound()
(i) Compute the a posteriori error bound for each set of parameters in the training set...
This is the MeshBase class.
Definition: mesh_base.h:80
virtual void zero()=0
Set all entries to zero.
virtual void elem_fe_reinit(const std::vector< Point > *const pts=nullptr)
Reinitializes interior FE objects on the current geometric element.
Definition: fem_context.C:1476
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...
void print_basis_function_orthogonality() const
Print out a matrix that shows the orthogonality of the RB basis functions.
std::vector< std::unique_ptr< NumericVector< Number > > > non_dirichlet_Fq_vector
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 add(const numeric_index_type i, const numeric_index_type j, const T value)=0
Add value to the element (i,j).
virtual void load_training_set(const std::map< std::string, std::vector< RBParameter >> &new_training_set)
Overwrite the training parameters with new_training_set.
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 assemble_inner_product_matrix(SparseMatrix< Number > *input_matrix, bool apply_dof_constraints=true)
Assemble the inner product matrix and store it in input_matrix.
LinearSolver< Number > * extra_linear_solver
Also, we store a pointer to an extra linear solver.
FEType get_fe_type() const
Definition: fe_abstract.h:520
virtual void boundary_assembly(FEMContext &)
Perform the element boundary assembly.
Definition: elem_assembly.h:60
Generic sparse matrix.
Definition: vector_fe_ex5.C:46
This class handles the numbering of degrees of freedom on a mesh.
Definition: dof_map.h:179
virtual void scale(const T factor)=0
Scale each element of the vector by the given factor.
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...
virtual void load_basis_function(unsigned int i)
Load the i^th RB function into the RBConstruction solution vector.
std::unique_ptr< QBase > default_quadrature_rule(const unsigned int dim, const int extraorder=0) const
Definition: fe_type.C:34
This base class can be inherited from to provide interfaces to linear solvers from different packages...
std::vector< DenseVector< Number > > RB_Fq_vector
Dense vector for the RHS.
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...
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 ...
bool impose_internal_fluxes
Boolean flag to indicate whether we impose "fluxes" (i.e.
bool Fq_representor_innerprods_computed
A boolean flag to indicate whether or not the Fq representor norms have already been computed — used...
void set_training_random_seed(int seed)
Set the seed that is used to randomly generate training parameters.
bool use_empty_rb_solve_in_greedy
A boolean flag to indicate whether or not we initialize the Greedy algorithm by performing rb_solves ...
bool use_energy_inner_product
Boolean to indicate whether we&#39;re using the energy inner-product.
void libmesh_ignore(const Args &...)
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.
RBThetaExpansion & get_rb_theta_expansion()
Get a reference to the RBThetaExpansion object that that belongs to rb_eval.
bool get_preevaluate_thetas_flag() const
Get/set flag to pre-evaluate the theta functions.
const T & get(std::string_view) const
Definition: parameters.h:451
void set_rb_assembly_expansion(RBAssemblyExpansion &rb_assembly_expansion_in)
Set the rb_assembly_expansion object.
virtual Real l2_norm() const =0
virtual void zero()=0
Set all entries to 0.
const RBParameters & get_parameters_min() const
Get an RBParameters object that specifies the minimum allowable value for each parameter.
std::unique_ptr< SparseMatrix< Number > > non_dirichlet_inner_product_matrix
dof_id_type numeric_index_type
Definition: id_types.h:99
virtual void init(const char *name=nullptr)=0
Initialize data structures if not done so already.
virtual T el(const unsigned int i, const unsigned int j) const override final
Definition: dense_matrix.h:126
void update_greedy_param_list()
Update the list of Greedily chosen parameters with current_parameters.
Real rel_training_tolerance
Relative and absolute tolerances for training reduced basis using the Greedy scheme.
unsigned int _current_training_parameter_index
The current training parameter index during reduced basis training.
unsigned int n_linear_iterations() const
void svd(DenseVector< Real > &sigma)
Compute the singular value decomposition of the matrix.
unsigned int get_Nmax() const
Get/set Nmax, the maximum number of RB functions we are willing to compute.
RBAssemblyExpansion & get_rb_assembly_expansion()
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.
Definition: exodusII_io.C:2050
SparseMatrix< Number > * get_inner_product_matrix()
Get a pointer to inner_product_matrix.
void get_transpose(DenseMatrix< T > &dest) const
Put the tranposed matrix into dest.
virtual void update_RB_system_matrices()
Compute the reduced basis matrices for the current basis.
NumericVector< Number > & get_basis_function(unsigned int i)
Get a reference to the i^th basis function.
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.
bool store_untransformed_basis
Boolean flag to indicate whether we store a second copy of the basis without constraints or dof trans...
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.
numeric_index_type get_last_local_training_index() const
Get the last local index of the training parameters.
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
Definition: system.h:1655
const std::vector< Number > & get_evaluated_thetas(unsigned int training_parameter_index) const
Return the evaluated theta functions at the given training parameter index.
numeric_index_type get_local_n_training_samples() const
Get the total number of training samples local to this processor.
virtual void update_system()
Update the system after enriching the RB space; this calls a series of functions to update the system...
Real get_abs_training_tolerance() const
This class extends FEMContext in order to provide extra data required to perform local element residu...
libmesh_assert(ctx)
bool is_constrained_dof(const dof_id_type dof) const
Definition: dof_map.h:2426
virtual SparseMatrix< Number > & get_matrix_for_output_dual_solves()
Return the matrix for the output residual dual norm solves.
The Tri3Subdivision element is a three-noded subdivision surface shell element used in mechanics calc...
NumericVector< Number > * get_non_dirichlet_Fq(unsigned int q)
Get a pointer to non-Dirichlet Fq.
virtual Real get_RB_error_bound()
const std::vector< dof_id_type > & get_dof_indices() const
Accessor for element dof indices.
Definition: diff_context.h:363
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...
CouplingMatrix * _dof_coupling
Degree of freedom coupling.
Definition: dof_map.h:1741
const RBParameters & get_parameters() const
Get the current parameters.
void print_parameters() const
Print the current parameters.
SparseMatrix< Number > * get_non_dirichlet_Aq(unsigned int q)
Get a pointer to non_dirichlet_Aq.
void set_inner_product_assembly(ElemAssembly &inner_product_assembly_in)
Set the rb_assembly_expansion object.
std::vector< std::unique_ptr< SparseMatrix< Number > > > Aq_vector
Vector storing the Q_a matrices from the affine expansion.
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...
bool skip_residual_in_train_reduced_basis
Boolean flag to indicate if we skip residual calculations in train_reduced_basis. ...
void set_convergence_assertion_flag(bool flag)
Setter for the flag determining if convergence should be checked after each solve.
This class is part of the rbOOmit framework.
Definition: rb_parameters.h:52
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...
bool is_rb_eval_initialized() const
unsigned int delta_N
The number of basis functions that we add at each greedy step.
Real train_reduced_basis_with_greedy(const bool resize_rb_eval_data)
Train the reduced basis using the "Greedy algorithm.".
RBEvaluation * rb_eval
The current RBEvaluation object we are using to perform the Evaluation stage of the reduced basis met...
Real volume(const MeshBase &mesh, unsigned int dim=libMesh::invalid_uint)
Find the total volume of a mesh (interpreting that as area for dim = 2, or total arc length for dim =...
Definition: mesh_tools.C:1020
std::unique_ptr< NumericVector< Number > > _untransformed_solution
We also store a copy of the untransformed solution in order to create _untransformed_basis_functions...
numeric_index_type get_n_training_samples() const
Get the number of global training samples.
const DenseMatrix< Number > & get_elem_neighbor_jacobian() const
Const accessor for element-neighbor Jacobian.
virtual void close()=0
Calls the NumericVector&#39;s internal assembly routines, ensuring that the values are consistent across ...
This class implements a C++ interface to the XDR (eXternal Data Representation) format.
Definition: xdr_cxx.h:67
bool dg_terms_are_active() const
Are the DG terms active, i.e.
Real _final_linear_residual
The final residual for the linear system Ax=b.
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...
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...
virtual Real rb_solve(unsigned int N)
Perform online solve with the N RB basis functions, for the set of parameters in current_params, where 0 <= N <= RB_size.
This class is part of the rbOOmit framework.
Definition: rb_evaluation.h:50
virtual void post_process_truth_solution()
Similarly, provide an opportunity to post-process the truth solution after the solve is complete...
virtual unsigned int n_sides() const =0
bool set_parameters(const RBParameters &params)
Set the current parameters to params The parameters are checked for validity; an error is thrown if t...
virtual LinearConvergenceReason get_converged_reason() const =0
const DenseMatrix< Number > & get_neighbor_elem_jacobian() const
Const accessor for element-neighbor Jacobian.
const Elem * neighbor_ptr(unsigned int i) const
Definition: elem.h:2612
void set_current_training_parameter_index(unsigned int index)
const DenseVector< Number > & get_elem_residual() const
Const accessor for element residual.
Definition: diff_context.h:242
virtual void update()
Update the local values to reflect the solution on neighboring processors.
Definition: system.C:498
void set_abs_training_tolerance(Real new_training_tolerance)
Get/set the absolute tolerance for the basis training.
std::vector< DenseMatrix< Number > > RB_Aq_vector
Dense matrices for the RB computations.
virtual void close()=0
Calls the SparseMatrix&#39;s internal assembly routines, ensuring that the values are consistent across p...
virtual void print_matlab(const std::string &filename="") const
Print the contents of the vector in Matlab&#39;s sparse matrix format.
bool store_non_dirichlet_operators
Boolean flag to indicate whether we store a second copy of each affine operator and vector which does...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual void clear() override
Clear all the data structures associated with the system.
virtual std::unique_ptr< Elem > side_ptr(unsigned int i)=0
unsigned int get_n_output_terms(unsigned int output_index) const
Get the number of affine terms associated with the specified output.
ElemAssembly * inner_product_assembly
Pointer to inner product assembly.
virtual void reuse_preconditioner(bool)
Set the same_preconditioner flag, which indicates if we reuse the same preconditioner for subsequent ...
unsigned char side
Current side for side_* to examine.
Definition: fem_context.h:1013
virtual std::string system_type() const override
void check_convergence(LinearSolver< Number > &input_solver)
Check if the linear solver reports convergence.
void max(const T &r, T &o, Request &req) const
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...
auto norm(const T &a)
Definition: tensor_tools.h:74
void set_value(const std::string &param_name, Real value)
Set the value of the specified parameter.
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.
std::vector< std::unique_ptr< NumericVector< Number > > > Fq_vector
Vector storing the Q_f vectors in the affine decomposition of the right-hand side.
This class stores the set of ElemAssembly functor objects that define the "parameter-independent expa...
OStreamProxy out
virtual bool is_serial_training_type(const std::string &RB_training_type_in)
SparseMatrix< Number > * matrix
The system matrix.
virtual unsigned int get_n_basis_functions() const
Get the current number of basis functions.
ElemAssembly & get_A_assembly(unsigned int q)
Return a reference to the specified A_assembly object.
ElemAssembly provides a per-element (interior and boundary) assembly functionality.
Definition: elem_assembly.h:38
virtual void preevaluate_thetas()
std::vector< Number > truth_outputs
Vector storing the truth output values from the most recent truth solve.
SparseMatrix< Number > * get_non_dirichlet_inner_product_matrix_if_avail()
Get the non-Dirichlet inner-product matrix if it&#39;s available, otherwise get the inner-product matrix ...
static const bool value
Definition: xdr_io.C:55
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_Nmax(unsigned int Nmax)
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 ...
void set_rel_training_tolerance(Real new_training_tolerance)
Get/set the relative tolerance for the basis training.
std::vector< std::vector< std::unique_ptr< NumericVector< Number > > > > non_dirichlet_outputs_vector
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
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.
virtual void assemble_all_output_vectors()
Assemble and store the output vectors.
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...
Parameters parameters
Data structure holding arbitrary parameters.
virtual unsigned int size() const override final
Definition: dense_vector.h:104
bool _preevaluate_thetas_flag
Flag to indicate if we preevaluate the theta functions.
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
bool serial_training_set
This boolean flag indicates whether or not the training set should be the same on all processors...
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.
virtual void process_parameters_file(const std::string &parameters_filename)
Read in from the file specified by parameters_filename and set the this system&#39;s member variables acc...
RBAssemblyExpansion * rb_assembly_expansion
This member holds the (parameter independent) assembly functors that define the "affine expansion" of...
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...
Definition: fem_context.h:277
NumericVector< Number > * get_output_vector(unsigned int n, unsigned int q_l)
Get a pointer to the n^th output.
virtual bool check_if_zero_truth_solve() const
RBEvaluation & get_rb_evaluation()
Get a reference to the RBEvaluation object.
virtual void assemble_misc_matrices()
Assemble and store all the inner-product matrix, the constraint matrix (for constrained problems) and...
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 set(const numeric_index_type i, const T value)=0
Sets v(i) = value.
const DenseMatrix< Number > & get_elem_elem_jacobian() const
Const accessor for element-element Jacobian.
const std::string & name() const
Definition: system.h:2385
dof_id_type first_dof(const processor_id_type proc) const
Definition: dof_map_base.h:185
virtual void load_rb_solution()
Load the RB solution from the most recent solve with rb_eval into this system&#39;s solution vector...
void set_rb_evaluation(RBEvaluation &rb_eval_in)
Set the RBEvaluation object.
unsigned int n_vars() const
Definition: system.C:2674
unsigned int n() const
virtual void add(const numeric_index_type i, const T value)=0
Adds value to the vector entry specified by i.
virtual void compute_output_dual_innerprods()
Compute and store the dual norm of each output functional.
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
processor_id_type processor_id() const
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.
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...
std::unique_ptr< LinearSolver< Number > > linear_solver
This class handles all the details of interfacing with various linear algebra packages like PETSc or ...
bool output_dual_innerprods_computed
A boolean flag to indicate whether or not the output dual norms have already been computed — used to...
const DofMap & get_dof_map() const
Definition: system.h:2417
processor_id_type processor_id() const
Definition: dof_object.h:881
void train_reduced_basis_with_POD()
Train the reduced basis using Proper Orthogonal Decomposition (POD).
unsigned int get_n_params() const
Get the number of parameters.
virtual void enrich_RB_space()
Add a new basis function to the RB space.
virtual void assemble_all_affine_vectors()
Assemble and store the affine RHS vectors.
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
virtual void attach_quadrature_rule(QBase *q)=0
Provides the class with the quadrature rule.
NumericVector< Number > * get_non_dirichlet_output_vector(unsigned int n, unsigned int q_l)
Get a pointer to non-Dirichlet output vector.
virtual void allocate_data_structures()
Helper function that actually allocates all the data structures required by this class.
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
RBThetaExpansion & get_rb_theta_expansion()
Get a reference to the rb_theta_expansion.
Definition: rb_evaluation.C:85
bool _preevaluate_thetas_completed
Flag to indicate if the preevaluate_thetas function has been called, since this allows us to avoid ca...
This class forms the foundation from which generic finite elements may be derived.
SparseMatrix< Number > * get_non_dirichlet_Aq_if_avail(unsigned int q)
Get a pointer to non_dirichlet_Aq if it&#39;s available, otherwise get Aq.
virtual void clear()
Clear all the data structures associated with the system.
bool is_discrete_parameter(const std::string &mu_name) const
Is parameter mu_name discrete?
unsigned int Nmax
Maximum number of reduced basis functions we are willing to use.
virtual void assemble_affine_expansion(bool skip_matrix_assembly, bool skip_vector_assembly)
Assemble the matrices and vectors for this system.
std::vector< std::vector< std::vector< Number > > > Aq_Aq_representor_innerprods
void print_discrete_parameter_values() const
Print out all the discrete parameter values.
uint8_t dof_id_type
Definition: id_types.h:67
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
std::vector< std::vector< std::unique_ptr< NumericVector< Number > > > > outputs_vector
The libMesh vectors that define the output functionals.
This class defines a coupling matrix.
virtual void assemble_all_affine_operators()
Assemble and store all Q_a affine operators as well as the inner-product matrix.
virtual void localize(std::vector< T > &v_local) const =0
Creates a copy of the global vector in the local vector v_local.
std::vector< std::vector< DenseVector< Number > > > RB_output_vectors
The vectors storing the RB output vectors.