libMesh
optimization_ex1.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 
19 
20 // <h1>Optimization Example 1 - Optimization of a quadratic objective function</h1>
21 // \author David Knezevic
22 // \date 2015
23 //
24 // In this example we demonstrate how to use OptimizationSystem to
25 // solve the optimization problem:
26 // min_U 0.5*U^T A U - U^T F
27 // We enforce Dirichlet constraints on U so that the minimization is
28 // well-posed. But note that we do not use OptimizationSystem's
29 // interface for imposing constraints in this case, so we can use an
30 // unconstrained solver (e.g. TAO's "Newton's method with line search"
31 // for unconstrained optimization is the default choice).
32 
33 // C++ include files that we need
34 #include <iostream>
35 
36 // libMesh includes
37 #include "libmesh/libmesh.h"
38 #include "libmesh/mesh.h"
39 #include "libmesh/mesh_generation.h"
40 #include "libmesh/exodusII_io.h"
41 #include "libmesh/equation_systems.h"
42 #include "libmesh/fe.h"
43 #include "libmesh/quadrature_gauss.h"
44 #include "libmesh/dof_map.h"
45 #include "libmesh/sparse_matrix.h"
46 #include "libmesh/numeric_vector.h"
47 #include "libmesh/dense_matrix.h"
48 #include "libmesh/dense_vector.h"
49 #include "libmesh/elem.h"
50 #include "libmesh/fpe_disabler.h"
51 #include "libmesh/boundary_info.h"
52 #include "libmesh/string_to_enum.h"
53 #include "libmesh/getpot.h"
54 #include "libmesh/zero_function.h"
55 #include "libmesh/dirichlet_boundaries.h"
56 #include "libmesh/optimization_system.h"
57 #include "libmesh/optimization_solver.h"
58 
59 // Bring in everything from the libMesh namespace
60 using namespace libMesh;
61 
62 
71 {
72 private:
73 
78 
79 public:
80 
85 
91  void assemble_A_and_F();
92 
96  virtual Number objective (const NumericVector<Number> & soln,
97  OptimizationSystem & /*sys*/);
98 
102  virtual void gradient (const NumericVector<Number> & soln,
103  NumericVector<Number> & grad_f,
104  OptimizationSystem & /*sys*/);
105 
109  virtual void hessian (const NumericVector<Number> & soln,
110  SparseMatrix<Number> & H_f,
111  OptimizationSystem & /*sys*/);
112 
119 
125 };
126 
128  _sys(sys_in)
129 {}
130 
132 {
133  A_matrix->zero();
134  F_vector->zero();
135 
136  const MeshBase & mesh = _sys.get_mesh();
137 
138  const unsigned int dim = mesh.mesh_dimension();
139  const unsigned int u_var = _sys.variable_number ("u");
140 
141  const DofMap & dof_map = _sys.get_dof_map();
142  FEType fe_type = dof_map.variable_type(u_var);
143  std::unique_ptr<FEBase> fe (FEBase::build(dim, fe_type));
144  QGauss qrule (dim, fe_type.default_quadrature_order());
145  fe->attach_quadrature_rule (&qrule);
146 
147  const std::vector<Real> & JxW = fe->get_JxW();
148  const std::vector<std::vector<Real>> & phi = fe->get_phi();
149  const std::vector<std::vector<RealGradient>> & dphi = fe->get_dphi();
150 
151  std::vector<dof_id_type> dof_indices;
152 
155 
156  for (const auto & elem : mesh.active_local_element_ptr_range())
157  {
158  dof_map.dof_indices (elem, dof_indices);
159 
160  const unsigned int n_dofs = dof_indices.size();
161 
162  fe->reinit (elem);
163 
164  Ke.resize (n_dofs, n_dofs);
165  Fe.resize (n_dofs);
166 
167  for (unsigned int qp=0; qp<qrule.n_points(); qp++)
168  {
169  for (unsigned int dof_i=0; dof_i<n_dofs; dof_i++)
170  {
171  for (unsigned int dof_j=0; dof_j<n_dofs; dof_j++)
172  {
173  Ke(dof_i, dof_j) += JxW[qp] * (dphi[dof_j][qp]* dphi[dof_i][qp]);
174  }
175  Fe(dof_i) += JxW[qp] * phi[dof_i][qp];
176  }
177  }
178 
179 #ifdef LIBMESH_ENABLE_CONSTRAINTS
180  // This will zero off-diagonal entries of Ke corresponding to
181  // Dirichlet dofs.
182  dof_map.constrain_element_matrix_and_vector (Ke, Fe, dof_indices);
183 
184  // We want the diagonal of constrained dofs to be zero too
185  for (unsigned int local_dof_index=0; local_dof_index<n_dofs; local_dof_index++)
186  {
187  dof_id_type global_dof_index = dof_indices[local_dof_index];
188  if (dof_map.is_constrained_dof(global_dof_index))
189  {
190  Ke(local_dof_index, local_dof_index) = 0.;
191  }
192  }
193 #endif // LIBMESH_ENABLE_CONSTRAINTS
194 
195  A_matrix->add_matrix (Ke, dof_indices);
196  F_vector->add_vector (Fe, dof_indices);
197  }
198 
199  A_matrix->close();
200  F_vector->close();
201 }
202 
204  OptimizationSystem & /*sys*/)
205 {
206  std::unique_ptr<NumericVector<Number>> AxU = soln.zero_clone();
207 
208  A_matrix->vector_mult(*AxU, soln);
209 
210  Number
211  UTxAxU = AxU->dot(soln),
212  UTxF = F_vector->dot(soln);
213 
214  return 0.5 * UTxAxU - UTxF;
215 }
216 
218  NumericVector<Number> & grad_f,
219  OptimizationSystem & /*sys*/)
220 {
221  grad_f.zero();
222 
223  // Since we've enforced constraints on soln, A and F,
224  // this automatically sets grad_f to zero for constrained
225  // dofs.
226  A_matrix->vector_mult(grad_f, soln);
227  grad_f.add(-1, *F_vector);
228 }
229 
230 
232  SparseMatrix<Number> & H_f,
233  OptimizationSystem & /*sys*/)
234 {
235  LOG_SCOPE("hessian()", "AssembleOptimization");
236  H_f.zero();
237  H_f.add(1., *A_matrix);
238 }
239 
240 
241 int main (int argc, char ** argv)
242 {
243  LibMeshInit init (argc, argv);
244 
245 #ifndef LIBMESH_HAVE_PETSC_TAO
246 
247  libmesh_example_requires(false, "PETSc >= 3.5.0 with built-in TAO support");
248 
249 #elif !defined(LIBMESH_ENABLE_GHOSTED)
250 
251  libmesh_example_requires(false, "--enable-ghosted");
252 
253 #elif LIBMESH_USE_COMPLEX_NUMBERS
254 
255  // According to
256  // http://www.mcs.anl.gov/research/projects/tao/documentation/installation.html
257  // TAO & PETSc-complex are currently mutually exclusive
258  libmesh_example_requires(false, "PETSc >= 3.5.0 with built-in TAO support & real-numbers only");
259 
260 #endif
261 
262  // We use a 2D domain.
263  libmesh_example_requires(LIBMESH_DIM > 1, "--disable-1D-only");
264 
265  // We use Dirichlet boundary conditions here
266 #ifndef LIBMESH_ENABLE_DIRICHLET
267  libmesh_example_requires(false, "--enable-dirichlet");
268 #endif
269 
270  if (libMesh::on_command_line ("--use-eigen"))
271  {
272  libMesh::err << "This example requires an OptimizationSolver, and therefore does not "
273  << "support --use-eigen on the command line."
274  << std::endl;
275  return 0;
276  }
277 
278  GetPot infile("optimization_ex1.in");
279  infile.parse_command_line(argc, argv);
280 
281  const std::string approx_order = infile("approx_order", "FIRST");
282  const std::string fe_family = infile("fe_family", "LAGRANGE");
283  const unsigned int n_elem = infile("n_elem", 10);
284 
285  Mesh mesh(init.comm());
287  n_elem,
288  n_elem,
289  -1., 1.,
290  -1., 1.,
291  QUAD9);
292 
293  mesh.print_info();
294 
295  EquationSystems equation_systems (mesh);
296 
297  OptimizationSystem & system =
298  equation_systems.add_system<OptimizationSystem> ("Optimization");
299 
300  // The default is to use PETSc/Tao solvers, but let the user change
301  // the optimization solver package on the fly.
302  {
303  const std::string optimization_solver_type = infile("optimization_solver_type",
304  "PETSC_SOLVERS");
305  SolverPackage sp = Utility::string_to_enum<SolverPackage>(optimization_solver_type);
306  std::unique_ptr<OptimizationSolver<Number>> new_solver =
308  system.optimization_solver.reset(new_solver.release());
309  }
310 
311  // Set tolerances and maximum iteration counts directly on the optimization solver.
312  system.optimization_solver->max_objective_function_evaluations = 128;
313  system.optimization_solver->objective_function_relative_tolerance = 1.e-4;
314  system.optimization_solver->verbose = true;
315 
316  AssembleOptimization assemble_opt(system);
317 
318  system.optimization_solver->objective_object = &assemble_opt;
319  system.optimization_solver->gradient_object = &assemble_opt;
320  system.optimization_solver->hessian_object = &assemble_opt;
321 
322  // system.matrix and system.rhs are used for the gradient and Hessian,
323  // so in this case we add an extra matrix and vector to store A and F.
324  // This makes it easy to write the code for evaluating the objective,
325  // gradient, and hessian.
326  system.add_matrix("A_matrix");
327  system.add_vector("F_vector");
328  assemble_opt.A_matrix = &system.get_matrix("A_matrix");
329  assemble_opt.F_vector = &system.get_vector("F_vector");
330 
331 #ifdef LIBMESH_ENABLE_DIRICHLET
332  unsigned int u_var = system.add_variable("u",
333  Utility::string_to_enum<Order> (approx_order),
334  Utility::string_to_enum<FEFamily>(fe_family));
335 
336  // Apply Dirichlet constraints. This will be used to apply constraints
337  // to the objective function, gradient and Hessian.
338  ZeroFunction<> zf;
339 
340  // Most DirichletBoundary users will want to supply a "locally
341  // indexed" functor
342  DirichletBoundary dirichlet_bc({0,1,2,3}, {u_var}, zf,
344  system.get_dof_map().add_dirichlet_boundary(dirichlet_bc);
345 #endif // LIBMESH_ENABLE_DIRICHLET
346 
347  equation_systems.init();
348  equation_systems.print_info();
349 
350  assemble_opt.assemble_A_and_F();
351 
352  // We need to close the matrix so that we can use it to store the
353  // Hessian during the solve.
354  system.get_system_matrix().close();
355 
356  // We can get division-by-zero from ILU within the TAO solver, but
357  // they can recover from it, so if we have FPEs turned on we should
358  // turn them off.
359  FPEDisabler disable_fpes;
360 
361  system.solve();
362 
363  // Print convergence information
364  system.optimization_solver->print_converged_reason();
365 
366 #ifdef LIBMESH_HAVE_EXODUS_API
367  std::stringstream filename;
368  ExodusII_IO (mesh).write_equation_systems("optimization_soln.exo",
369  equation_systems);
370 #endif
371 
372  return 0;
373 }
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
Definition: fe_type.h:196
OStreamProxy err
OptimizationSystem & _sys
Keep a reference to the OptimizationSystem.
The FPEDisabler class puts Floating-Point Exception (FPE) trapping on hold during its lifetime...
Definition: fpe_disabler.h:49
This is the EquationSystems class.
virtual Number objective(const NumericVector< Number > &soln, OptimizationSystem &)
Evaluate the objective function.
ConstFunction that simply returns 0.
Definition: zero_function.h:38
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:2330
Abstract base class to be used to calculate the objective function for optimization.
void dof_indices(const Elem *const elem, std::vector< dof_id_type > &di) const
Definition: dof_map.C:2164
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
AssembleOptimization(OptimizationSystem &sys_in)
Constructor.
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...
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 std::unique_ptr< NumericVector< T > > zero_clone() const =0
void resize(const unsigned int n)
Resize the vector.
Definition: dense_vector.h:396
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]...
virtual void solve() override
Solves the optimization problem.
const FEType & variable_type(const unsigned int c) const
Definition: dof_map.h:2220
void print_info(std::ostream &os=libMesh::out) const
Prints information about the equation systems, by default to libMesh::out.
MeshBase & mesh
NumericVector< Number > * F_vector
Vector for storing F.
This class allows one to associate Dirichlet boundary values with a given set of mesh boundary ids an...
Order default_quadrature_order() const
Definition: fe_type.h:371
NumericVector< Number > & add_vector(std::string_view vec_name, const bool projections=true, const ParallelType type=PARALLEL)
Adds the additional vector vec_name to this system.
Definition: system.C:768
int main(int argc, char **argv)
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.
virtual T dot(const NumericVector< T > &v) const =0
const MeshBase & get_mesh() const
Definition: system.h:2358
This is the MeshBase class.
Definition: mesh_base.h:75
virtual void zero()=0
Set all entries to zero.
unsigned int variable_number(std::string_view var) const
Definition: system.C:1609
virtual void add(const numeric_index_type i, const numeric_index_type j, const T value)=0
Add value to the element (i,j).
This class handles the numbering of degrees of freedom on a mesh.
Definition: dof_map.h:179
SparseMatrix< Number > * A_matrix
Sparse matrix for storing the matrix A.
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.
This class encapsulate all functionality required for assembling the objective function, gradient, and hessian.
virtual void zero()=0
Set all entries to 0.
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
This System subclass enables us to assemble an objective function, gradient, Hessian and bounds for o...
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 init(triangulateio &t)
Initializes the fields of t to nullptr/0 as necessary.
bool is_constrained_dof(const dof_id_type dof) const
Definition: dof_map.h:2258
unsigned int add_variable(std::string_view var, const FEType &type, const std::set< subdomain_id_type > *const active_subdomains=nullptr)
Adds the variable var to the list of variables for this system.
Definition: system.C:1357
virtual void close()=0
Calls the NumericVector&#39;s internal assembly routines, ensuring that the values are consistent across ...
std::unique_ptr< OptimizationSolver< Number > > optimization_solver
The OptimizationSolver that is used for performing the optimization.
Abstract base class to be used to calculate the gradient of an objective function.
virtual void hessian(const NumericVector< Number > &soln, SparseMatrix< Number > &H_f, OptimizationSystem &)
Evaluate the Hessian.
virtual void close()=0
Calls the SparseMatrix&#39;s internal assembly routines, ensuring that the values are consistent across p...
This base class can be inherited from to provide interfaces to optimization solvers from different pa...
virtual void gradient(const NumericVector< Number > &soln, NumericVector< Number > &grad_f, OptimizationSystem &)
Evaluate the gradient.
Abstract base class to be used to calculate the Hessian of an objective function. ...
void resize(const unsigned int new_m, const unsigned int new_n)
Resizes the matrix to the specified size and calls zero().
Definition: dense_matrix.h:895
This class implements specific orders of Gauss quadrature.
unsigned int mesh_dimension() const
Definition: mesh_base.C:372
void add_dirichlet_boundary(const DirichletBoundary &dirichlet_boundary)
Adds a copy of the specified Dirichlet boundary to the system.
bool on_command_line(std::string arg)
Definition: libmesh.C:987
virtual void init()
Initialize all the systems.
SolverPackage
Defines an enum for various linear solver packages.
void assemble_A_and_F()
The optimization problem we consider here is: min_U 0.5*U^T A U - U^T F.
virtual void add(const numeric_index_type i, const T value)=0
Adds value to the vector entry specified by i.
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.
The Mesh class is a thin wrapper, around the ReplicatedMesh class by default.
Definition: mesh.h:50
SparseMatrix< Number > & add_matrix(std::string_view mat_name, ParallelType type=PARALLEL, MatrixBuildType mat_build_type=MatrixBuildType::AUTOMATIC)
Adds the additional matrix mat_name to this system.
Definition: system.C:1010
const DofMap & get_dof_map() const
Definition: system.h:2374
const SparseMatrix< Number > & get_matrix(std::string_view mat_name) const
Definition: system.C:1124
const SparseMatrix< Number > & get_system_matrix() const
const NumericVector< Number > & get_vector(std::string_view vec_name) const
Definition: system.C:943
uint8_t dof_id_type
Definition: id_types.h:67