libMesh
reduced_basis_ex4.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 // rbOOmit: An implementation of the Certified Reduced Basis method.
19 // Copyright (C) 2009, 2010 David J. Knezevic
20 // This file is part of rbOOmit.
21 
22 
23 
24 // <h1>Reduced Basis Example 4 - Empirical Interpolation Method</h1>
25 // \author David Knezevic
26 // \date 2011
27 //
28 // In this example problem we develop a reduced basis approximation
29 // for a parametrized PDE that has "non-affine" parameter
30 // dependence. This requires the use of the Empirical Interpolation
31 // Method (EIM).
32 //
33 // We first use EIM to construct an affine approximation to the
34 // non-affine term, which is a parametrized function that is a
35 // Gaussian with "center" defined by the two parameters (mu_1, mu_2)
36 // \in [-1,1]^2. We then employ this EIM approximation in order to
37 // generate a reduced basis approximation for the parametrized PDE:
38 // -0.05 * Laplacian(u) = f(mu_1, mu_2), with zero Dirichlet boundary
39 // conditions.
40 
41 // Basic include file needed for the mesh functionality.
42 #include "libmesh/libmesh.h"
43 #include "libmesh/mesh.h"
44 #include "libmesh/mesh_generation.h"
45 #include "libmesh/equation_systems.h"
46 #include "libmesh/exodusII_io.h"
47 #include "libmesh/getpot.h"
48 #include "libmesh/rb_data_serialization.h"
49 #include "libmesh/rb_data_deserialization.h"
50 #include "libmesh/enum_solver_package.h"
51 
52 #include "eim_classes.h"
53 #include "rb_classes.h"
54 
55 using namespace libMesh;
56 
57 
58 int main (int argc, char ** argv)
59 {
60  // Initialize libMesh.
61  LibMeshInit init (argc, argv);
62 
63  // This example requires a linear solver package.
64  libmesh_example_requires(libMesh::default_solver_package() != INVALID_SOLVER_PACKAGE,
65  "--enable-petsc, --enable-trilinos, or --enable-eigen");
66 
67 #if !defined(LIBMESH_HAVE_XDR)
68  // We need XDR support to write out reduced bases
69  libmesh_example_requires(false, "--enable-xdr");
70 #elif defined(LIBMESH_DEFAULT_SINGLE_PRECISION)
71  // XDR binary support requires double precision
72  libmesh_example_requires(false, "double precision");
73 #elif defined(LIBMESH_DEFAULT_TRIPLE_PRECISION)
74  // I have no idea why long double isn't working here... [RHS]
75  libmesh_example_requires(false, "double precision");
76 #endif
77 
78  // Skip this 2D example if libMesh was compiled as 1D-only.
79  libmesh_example_requires(2 <= LIBMESH_DIM, "2D support");
80 
81  // This example requires libmesh to be configured with both
82  // DirichletBoundary and Cap'n Proto support.
83 #if !defined(LIBMESH_ENABLE_DIRICHLET) || !defined(LIBMESH_HAVE_CAPNPROTO)
84  libmesh_example_requires(false, "--enable-dirichlet --enable-capnp");
85 #else
86 
87  // Define the names of the input files we will read the problem properties from
88  std::string eim_parameters = "eim.in";
89  std::string rb_parameters = "rb.in";
90  std::string main_parameters = "reduced_basis_ex4.in";
91  GetPot infile(main_parameters);
92 
93  unsigned int n_elem = infile("n_elem", 1); // Determines the number of elements in the "truth" mesh
94  const unsigned int dim = 2; // The number of spatial dimensions
95 
96  // Read the "online_mode" flag from the command line
97  const int online_mode =
98  libMesh::command_line_next("-online_mode", 0),
99  eim_training_function_to_plot =
100  libMesh::command_line_next("-eim_training_function_to_plot", 0),
101  eim_basis_function_to_plot =
102  libMesh::command_line_next("-eim_basis_function_to_plot", 0);
103 
104  ReplicatedMesh mesh (init.comm(), dim);
106  n_elem, n_elem,
107  -1., 1.,
108  -1., 1.,
109  QUAD4);
110 
111  // Set to true the write binary (XDR) files with EIM data, false to write ASCII files.
112  bool eim_binary_io = true;
113 
114  if (!online_mode)
115  {
116  // First run the Offline stage for the EIM approximation.
117  {
118  libMesh::out << "Perform EIM training" << std::endl << std::endl;
119 
120  // Initialize the EquationSystems object for this mesh and attach
121  // the EIM and RB Construction objects
122  EquationSystems equation_systems (mesh);
123 
124  SimpleEIMConstruction & eim_construction =
125  equation_systems.add_system<SimpleEIMConstruction> ("EIM");
126 
127  // Initialize the data structures for the equation system.
128  equation_systems.init ();
129 
130  // Print out some information about the "truth" discretization
131  mesh.print_info();
132  equation_systems.print_info();
133 
134  // Initialize the EIM RBEvaluation object
135  SimpleEIMEvaluation eim_rb_eval(mesh.comm());
136 
137  // Set the rb_eval objects for the RBConstructions
138  eim_construction.set_rb_eim_evaluation(eim_rb_eval);
139 
140  // Read data from input file and print state
141  eim_construction.process_parameters_file(eim_parameters);
142  eim_construction.print_info();
143 
144  // Perform the EIM Greedy and write out the data
145  eim_construction.initialize_eim_construction();
146  eim_construction.train_eim_approximation();
147 
148  RBDataSerialization::RBEIMEvaluationSerialization rb_eim_eval_writer(eim_rb_eval);
149  rb_eim_eval_writer.write_to_file("rb_eim_eval.bin");
150 
151  // Write out the EIM basis functions
152  eim_rb_eval.write_out_basis_functions("eim_data", eim_binary_io);
153  }
154 
155  {
156  libMesh::out << std::endl << "Perform RB training" << std::endl << std::endl;
157 
158  // Initialize the EquationSystems object for this mesh and attach
159  // the EIM and RB Construction objects
160  EquationSystems equation_systems (mesh);
161 
162  SimpleEIMConstruction & eim_construction =
163  equation_systems.add_system<SimpleEIMConstruction> ("EIM");
164  SimpleRBConstruction & rb_construction =
165  equation_systems.add_system<SimpleRBConstruction> ("RB");
166 
167  // Initialize the data structures for the equation system.
168  equation_systems.init ();
169 
170  // Print out some information about the "truth" discretization
171  mesh.print_info();
172  equation_systems.print_info();
173 
174  // Initialize the standard RBEvaluation object
175  SimpleRBEvaluation rb_eval(mesh.comm());
176 
177  // Initialize the EIM RBEvaluation object
178  SimpleEIMEvaluation eim_rb_eval(mesh.comm());
179 
180  // Set the rb_eval objects for the RBConstructions
181  eim_construction.set_rb_eim_evaluation(eim_rb_eval);
182  rb_construction.set_rb_evaluation(rb_eval);
183 
184  RBDataDeserialization::RBEIMEvaluationDeserialization rb_eim_eval_reader(eim_rb_eval);
185  rb_eim_eval_reader.read_from_file("rb_eim_eval.bin");
186  eim_rb_eval.read_in_basis_functions(rb_construction, "eim_data", eim_binary_io);
187 
188  // Read data from input file and print state
189  rb_construction.process_parameters_file(rb_parameters);
190 
191  // attach the EIM theta objects to the RBConstruction and RBEvaluation objects
192  eim_rb_eval.initialize_eim_theta_objects();
193  rb_eval.get_rb_theta_expansion().attach_multiple_F_theta(eim_rb_eval.get_eim_theta_objects());
194 
195  // attach the EIM assembly objects to the RBConstruction object
196  eim_construction.initialize_eim_assembly_objects();
197  rb_construction.get_rb_assembly_expansion().attach_multiple_F_assembly(eim_construction.get_eim_assembly_objects());
198 
199  // Print out the state of rb_construction now that the EIM objects have been attached
200  rb_construction.print_info();
201 
202  // Need to initialize _after_ EIM greedy so that
203  // the system knows how many affine terms there are
204  rb_construction.initialize_rb_construction();
205  rb_construction.train_reduced_basis();
206 
207  RBDataSerialization::RBEvaluationSerialization rb_eval_writer(rb_construction.get_rb_evaluation());
208  rb_eval_writer.write_to_file("rb_eval.bin");
209 
210  rb_construction.get_rb_evaluation().write_out_basis_functions(rb_construction,
211  "rb_data");
212  }
213  }
214  else // online mode
215  {
216  SimpleEIMEvaluation eim_rb_eval(mesh.comm());
217  SimpleRBEvaluation rb_eval(mesh.comm());
218 
219  RBDataDeserialization::RBEIMEvaluationDeserialization rb_eim_eval_reader(eim_rb_eval);
220  rb_eim_eval_reader.read_from_file("rb_eim_eval.bin");
221 
222  // attach the EIM theta objects to rb_eval objects
223  eim_rb_eval.initialize_eim_theta_objects();
224  rb_eval.get_rb_theta_expansion().attach_multiple_F_theta(eim_rb_eval.get_eim_theta_objects());
225 
226  // Read in the offline data for rb_eval
228  rb_eval_reader.read_from_file("rb_eval.bin", /*read_error_bound_data*/ true);
229 
230  // Get the parameters at which we will do a reduced basis solve
231  Real online_center_x = infile("online_center_x", 0.);
232  Real online_center_y = infile("online_center_y", 0.);
233  RBParameters online_mu;
234  online_mu.push_back_value("center_x", online_center_x);
235  online_mu.push_back_value("center_y", online_center_y);
236 
237  // Testing: Add secondary center_x and center_y values,
238  // corresponding to e.g. a different time step or load step.
239  online_mu.push_back_value("center_x", 0.5);
240  online_mu.push_back_value("center_y", 0.5);
241 
242  // Add 3rd (center_x, center_y) values. For debugging purposes,
243  // we want the number of "samples" (3) to be different from the
244  // number of parameters (2).
245  online_mu.push_back_value("center_x", -0.25);
246  online_mu.push_back_value("center_y", -0.25);
247 
248  // Here we are going to pre-evaluate the thetas and pass them in
249  // to rb_solve() in a loop. Note that the rb_solve doesn't
250  // explicitly need the parameters ("mus"), it just needs the
251  // evaluated functions of parameters ("thetas"), but we can
252  // perform an rb_solve() with either.
253  rb_eval.set_parameters(online_mu);
254  rb_eval.print_parameters();
255 
256  // When performing an rb_solve() with "mu" values, we only
257  // support single-valued RBParameters objects. In this case,
258  // since the RBParameters object stores multiple "samples", we
259  // take the approach of pre-evaluating the thetas for each
260  // parameter while calling the rb_solve() in a loop.
261  const RBThetaExpansion & rb_theta_expansion = rb_eval.get_rb_theta_expansion();
262 
263  // Single-entry vector which makes calling the vector-overrides
264  // of eval_A_theta(), eval_F_theta(), etc. easier.
265  std::vector<RBParameters> mu_vec = {online_mu};
266 
267  // 1.) Evaluate and store "A" thetas at all samples:
268  std::vector<std::vector<Number>> all_A(rb_theta_expansion.get_n_A_terms());
269  for (unsigned int q_a=0; q_a<rb_theta_expansion.get_n_A_terms(); q_a++)
270  {
271  // Here we call the version of eval_A_theta() taking a
272  // vector, so we evaluate theta(mu) at all samples
273  // simultaneously.
274  all_A[q_a] = rb_theta_expansion.eval_A_theta(q_a, mu_vec);
275 
276  // This size of each A_q vector here is:
277  // sum_i mu_vec[i].n_samples()
278  //
279  // Each A_q vector contains the logically 2D ragged array of
280  // values (theta[i], sample[j(i)]), arranged in "row-major"
281  // format, where the number of samples defined by each mu
282  // object can vary, and they are all concatenated together
283  // to produce one long list of samples.
284  //
285  // Example: suppose that there are 2 mu values containing
286  // the same sets of parameters, with 5 samples defined in the
287  // first and 10 samples defined in the second. Then, the A_q
288  // vector will look like:
289  //
290  // {Theta(mu_vec[0], sample_0), Theta(mu_vec[0], sample_1), ... Theta(mu_vec[0], sample_4),
291  // Theta(mu_vec[1], sample_0), Theta(mu_vec[1], sample_1), ... Theta(mu_vec[1], sample_4), ..., Theta(mu_vec[1], sample_9)}
292  //
293  // Note: this example is unusual in that it would be
294  // conceptually much simpler to have either:
295  // (i) A vector of 15 single-sample RBParameters objects, or
296  // (ii) A single RBParameters object with 15 samples defined in it.
297  // but we can also support a combination of approaches (i)
298  // and (ii) by concatenation, if necessary.
299  }
300 
301  // 2.) Evaluate "F" thetas at all samples:
302  std::vector<std::vector<Number>> all_F(rb_theta_expansion.get_n_F_terms());
303  for (unsigned int q_f=0; q_f<rb_theta_expansion.get_n_F_terms(); q_f++)
304  all_F[q_f] = rb_theta_expansion.eval_F_theta(q_f, mu_vec);
305 
306  // The eval_F_theta() calls above use "EIM solves" from eim_rb_eval, hence we
307  // can now print out the EIM error indicator values.
308  const auto & eim_error_indicators =
309  eim_rb_eval.get_rb_eim_error_indicators();
310 
311  libMesh::out << std::scientific;
312  for (auto idx : index_range(eim_error_indicators))
313  libMesh::out << "EIM (error indicator, normalization factor) for step "
314  << idx << ": "
315  << "(" << eim_error_indicators[idx].first
316  << ", " << eim_error_indicators[idx].second
317  << ")" << std::endl;
318  libMesh::out << std::endl;
319 
320  // 3.) Evaluate "output" thetas at all samples: in this case, the
321  // output is particularly simple (does not depend on mu) so this
322  // should just be a vector of all 1s.
323  std::vector<std::vector<Number>> all_outputs(rb_theta_expansion.get_total_n_output_terms());
324  {
325  unsigned int output_counter = 0;
326  for (unsigned int n=0; n<rb_theta_expansion.get_n_outputs(); n++)
327  for (unsigned int q_l=0; q_l<rb_theta_expansion.get_n_output_terms(n); q_l++)
328  all_outputs[output_counter++] =
329  rb_theta_expansion.eval_output_theta(n, q_l, mu_vec);
330  }
331 
332  // The total number of thetas is the sum of the "A", "F", and
333  // "output" thetas.
334  unsigned int n_thetas =
335  rb_theta_expansion.get_n_A_terms() +
336  rb_theta_expansion.get_n_F_terms() +
337  rb_theta_expansion.get_total_n_output_terms();
338 
339  // Allocate enough space to store all the evaluated theta values for this RBThetaExpansion
340  std::vector<Number> evaluated_thetas(n_thetas);
341 
342  // The RBConstruction object is used for plotting reduced-basis solutions (visualization)
343  EquationSystems equation_systems (mesh);
344 
345  SimpleRBConstruction & rb_construction =
346  equation_systems.add_system<SimpleRBConstruction> ("RB");
347 
348  equation_systems.init ();
349 
350  // Tell the RBConstruction object about the RBEvaluation object
351  // and read in the RB basis functions.
352  rb_construction.set_rb_evaluation(rb_eval);
353  rb_eval.read_in_basis_functions(rb_construction, "rb_data");
354 
355  // Loop over each sample, fill the evaluated_thetas array, call rb_solve()
356  for (unsigned sample_idx=0; sample_idx<online_mu.n_samples(); ++sample_idx)
357  {
358  unsigned int counter = 0;
359 
360  // Set A Theta values for current sample
361  for (unsigned int q_a=0; q_a<rb_theta_expansion.get_n_A_terms(); q_a++)
362  evaluated_thetas[counter++] = all_A[q_a][sample_idx];
363 
364  // Set F Theta values for current sample
365  for (unsigned int q_f=0; q_f<rb_theta_expansion.get_n_F_terms(); q_f++)
366  evaluated_thetas[counter++] = all_F[q_f][sample_idx];
367 
368  // Set output Theta values for current sample
369  {
370  unsigned int output_counter = 0;
371  for (unsigned int n=0; n<rb_theta_expansion.get_n_outputs(); n++)
372  for (unsigned int q_l=0; q_l<rb_theta_expansion.get_n_output_terms(n); q_l++)
373  evaluated_thetas[counter++] = all_outputs[output_counter++][sample_idx];
374  }
375 
376  // Call rb_solve() for the current thetas
377  libMesh::out << "Performing solve for step " << sample_idx << std::endl;
378  rb_eval.rb_solve(rb_eval.get_n_basis_functions(), &evaluated_thetas);
379 
380  // Print the output as well as the corresponding output error bound
381  for (auto i : index_range(rb_eval.RB_outputs))
382  libMesh::out << "Output value " << i << " = " << rb_eval.RB_outputs[i]
383  << ", error bound " << i << " = " << rb_eval.RB_output_error_bounds[i]
384  << std::endl;
385 
386  // Write an exo file to visualize this solution
387  rb_construction.load_rb_solution();
388 #ifdef LIBMESH_HAVE_EXODUS_API
389  ExodusII_IO(mesh).write_equation_systems("RB_sol_" + std::to_string(sample_idx) + ".e", equation_systems);
390 #endif
391  } // end for (sample)
392  }
393 
394 #endif // LIBMESH_ENABLE_DIRICHLET
395 }
void write_to_file(const std::string &path, bool use_packing=false)
Write the Cap&#39;n&#39;Proto buffer to disk.
T command_line_next(std::string name, T default_value)
Use GetPot&#39;s search()/next() functions to get following arguments from the command line...
Definition: libmesh.C:1078
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.
The ReplicatedMesh class is derived from the MeshBase class, and is used to store identical copies of...
virtual Number eval_A_theta(unsigned int q, const RBParameters &mu) const
Evaluate theta_q_a at the current parameter.
unsigned int get_n_F_terms() const
Get Q_f, the number of terms in the affine expansion for the right-hand side.
int main(int argc, char **argv)
void initialize_eim_construction()
Perform initialization of this object to prepare for running train_eim_approximation().
This class serializes an RBEvaluation object using the Cap&#39;n Proto library.
dof_id_type n_elem(const MeshBase::const_element_iterator &begin, const MeshBase::const_element_iterator &end)
Count up the number of elements of a specific type (as defined by an iterator range).
Definition: mesh_tools.C:969
unsigned int dim
The ExodusII_IO class implements reading meshes in the ExodusII file format from Sandia National Labs...
Definition: exodusII_io.h:52
virtual Real train_eim_approximation()
Generate the EIM approximation for the specified parametrized function using either POD or the Greedy...
void print_info(std::ostream &os=libMesh::out) const
Prints information about the equation systems, by default to libMesh::out.
MeshBase & mesh
virtual void process_parameters_file(const std::string &parameters_filename)
Read parameters in from file and set up this system accordingly.
unsigned int get_n_A_terms() const
Get Q_a, the number of terms in the affine expansion for the bilinear form.
const Parallel::Communicator & comm() const
void build_square(UnstructuredMesh &mesh, const unsigned int nx, const unsigned int ny, const Real xmin=0., const Real xmax=1., const Real ymin=0., const Real ymax=1., const ElemType type=INVALID_ELEM, const bool gauss_lobatto_grid=false)
A specialized build_cube() for 2D meshes.
The LibMeshInit class, when constructed, initializes the dependent libraries (e.g.
Definition: libmesh.h:90
The libMesh namespace provides an interface to certain functionality in the library.
This class serializes an RBEIMEvaluation object using the Cap&#39;n Proto library.
unsigned int get_n_outputs() const
Get n_outputs, the number output functionals.
This class stores the set of RBTheta functor objects that define the "parameter-dependent expansion" ...
virtual void print_info()
Print out info that describes the current setup of this RBConstruction.
SolverPackage default_solver_package()
Definition: libmesh.C:1117
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:2033
void print_info(std::ostream &os=libMesh::out, const unsigned int verbosity=0, const bool global=true) const
Prints relevant information about the mesh.
Definition: mesh_base.C:1562
void read_from_file(const std::string &path, bool read_error_bound_data, bool use_packing=false)
Read the Cap&#39;n&#39;Proto buffer from disk.
void init(triangulateio &t)
Initializes the fields of t to nullptr/0 as necessary.
virtual Number eval_F_theta(unsigned int q, const RBParameters &mu) const
Evaluate theta_q_f at the current parameter.
This class is part of the rbOOmit framework.
Definition: rb_parameters.h:52
unsigned int n_samples() const
Returns the number of samples stored for all parameters.
void set_rb_eim_evaluation(RBEIMEvaluation &rb_eim_eval_in)
Set the RBEIMEvaluation object.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
void write_to_file(const std::string &path, bool use_packing=false)
Write the Cap&#39;n&#39;Proto buffer to disk.
unsigned int get_n_output_terms(unsigned int output_index) const
Get the number of affine terms associated with the specified output.
virtual void initialize_eim_assembly_objects()
Build a vector of ElemAssembly objects that accesses the basis functions stored in this RBEIMConstruc...
std::vector< std::unique_ptr< ElemAssembly > > & get_eim_assembly_objects()
unsigned int get_total_n_output_terms() const
Returns the total number of affine terms associated with all outputs.
void read_from_file(const std::string &path, bool use_packing=false)
Read the Cap&#39;n&#39;Proto buffer from disk.
OStreamProxy out
This class de-serializes an RBEvaluation object using the Cap&#39;n Proto library.
virtual void init()
Initialize all the systems.
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.
virtual System & add_system(std::string_view system_type, std::string_view name)
Add the system of type system_type named name to the systems array.
void push_back_value(const std::string &param_name, Real value)
Similar to set_value(name, index, value) but instead of specifying a particular index, just appends one more.
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:117
This class de-serializes a RBEIMEvaluation object using the Cap&#39;n Proto library.
unsigned int idx(const ElemType type, const unsigned int nx, const unsigned int i, const unsigned int j)
A useful inline function which replaces the macros used previously.