1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2020 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 #include "libmesh/libmesh_common.h"
21 
22 #if defined(LIBMESH_HAVE_PETSC_TAO) && !defined(LIBMESH_USE_COMPLEX_NUMBERS)
23 
24 
25 // C++ includes
26 
27 // Local Includes
28 #include "libmesh/libmesh_logging.h"
29 #include "libmesh/petsc_vector.h"
30 #include "libmesh/petsc_matrix.h"
31 #include "libmesh/dof_map.h"
32 #include "libmesh/tao_optimization_solver.h"
33 #include "libmesh/equation_systems.h"
34 
35 namespace libMesh
36 {
37 
38 //--------------------------------------------------------------------
39 // Functions with C linkage to pass to Tao. Tao will call these
40 // methods as needed.
41 //
42 // Since they must have C linkage they have no knowledge of a namespace.
43 // Give them an obscure name to avoid namespace pollution.
44 extern "C"
45 {
46 
47   //---------------------------------------------------------------
48   // This function is called by Tao to evaluate objective function at x
49   PetscErrorCode
__libmesh_tao_objective(Tao,Vec x,PetscReal * objective,void * ctx)50   __libmesh_tao_objective (Tao /*tao*/, Vec x, PetscReal * objective, void * ctx)
51   {
52     LOG_SCOPE("objective()", "TaoOptimizationSolver");
53 
54     PetscErrorCode ierr = 0;
55 
56     libmesh_assert(x);
57     libmesh_assert(objective);
58     libmesh_assert(ctx);
59 
60     // ctx should be a pointer to the solver (it was passed in as void *)
61     TaoOptimizationSolver<Number> * solver =
62       static_cast<TaoOptimizationSolver<Number> *> (ctx);
63 
64     OptimizationSystem & sys = solver->system();
65 
66     // We'll use current_local_solution below, so let's ensure that it's consistent
67     // with the vector x that was passed in.
68     PetscVector<Number> & X_sys = *cast_ptr<PetscVector<Number> *>(sys.solution.get());
69     PetscVector<Number> X(x, sys.comm());
70 
71     // Perform a swap so that sys.solution points to the input vector
72     // "x", update sys.current_local_solution based on "x", then swap
73     // back.
74     X.swap(X_sys);
75     sys.update();
76     X.swap(X_sys);
77 
78     // Enforce constraints (if any) exactly on the
79     // current_local_solution.  This is the solution vector that is
80     // actually used in the computation of the objective function
81     // below, and is not locked by debug-enabled PETSc the way that
82     // the solution vector is.
83     sys.get_dof_map().enforce_constraints_exactly(sys, sys.current_local_solution.get());
84 
85     if (solver->objective_object != nullptr)
86       (*objective) = PS(solver->objective_object->objective(*(sys.current_local_solution), sys));
87     else
88       libmesh_error_msg("Objective function not defined in __libmesh_tao_objective");
89 
90     return ierr;
91   }
92 
93 
94 
95   //---------------------------------------------------------------
96   // This function is called by Tao to evaluate the gradient at x
97   PetscErrorCode
__libmesh_tao_gradient(Tao,Vec x,Vec g,void * ctx)98   __libmesh_tao_gradient(Tao /*tao*/, Vec x, Vec g, void * ctx)
99   {
100     LOG_SCOPE("gradient()", "TaoOptimizationSolver");
101 
102     PetscErrorCode ierr = 0;
103 
104     libmesh_assert(x);
105     libmesh_assert(g);
106     libmesh_assert(ctx);
107 
108     // ctx should be a pointer to the solver (it was passed in as void *)
109     TaoOptimizationSolver<Number> * solver =
110       static_cast<TaoOptimizationSolver<Number> *> (ctx);
111 
112     OptimizationSystem & sys = solver->system();
113 
114     // We'll use current_local_solution below, so let's ensure that it's consistent
115     // with the vector x that was passed in.
116     PetscVector<Number> & X_sys = *cast_ptr<PetscVector<Number> *>(sys.solution.get());
117     PetscVector<Number> X(x, sys.comm());
118 
119     // Perform a swap so that sys.solution points to the input vector
120     // "x", update sys.current_local_solution based on "x", then swap
121     // back.
122     X.swap(X_sys);
123     sys.update();
124     X.swap(X_sys);
125 
126     // We'll also pass the gradient in to the assembly routine
127     // so let's make a PETSc vector for that too.
128     PetscVector<Number> gradient(g, sys.comm());
129 
130     // Clear the gradient prior to assembly
131     gradient.zero();
132 
133     // Enforce constraints exactly on the current_local_solution.
134     sys.get_dof_map().enforce_constraints_exactly(sys, sys.current_local_solution.get());
135 
136     if (solver->gradient_object != nullptr)
137       solver->gradient_object->gradient(*(sys.current_local_solution), gradient, sys);
138     else
139       libmesh_error_msg("Gradient function not defined in __libmesh_tao_gradient");
140 
141     gradient.close();
142 
143     return ierr;
144   }
145 
146   //---------------------------------------------------------------
147   // This function is called by Tao to evaluate the Hessian at x
148   PetscErrorCode
__libmesh_tao_hessian(Tao,Vec x,Mat h,Mat pc,void * ctx)149   __libmesh_tao_hessian(Tao /*tao*/, Vec x, Mat h, Mat pc, void * ctx)
150   {
151     LOG_SCOPE("hessian()", "TaoOptimizationSolver");
152 
153     PetscErrorCode ierr = 0;
154 
155     libmesh_assert(x);
156     libmesh_assert(h);
157     libmesh_assert(pc);
158     libmesh_assert(ctx);
159 
160     // ctx should be a pointer to the solver (it was passed in as void *)
161     TaoOptimizationSolver<Number> * solver =
162       static_cast<TaoOptimizationSolver<Number> *> (ctx);
163 
164     OptimizationSystem & sys = solver->system();
165 
166     // We'll use current_local_solution below, so let's ensure that it's consistent
167     // with the vector x that was passed in.
168     PetscVector<Number> & X_sys = *cast_ptr<PetscVector<Number> *>(sys.solution.get());
169     PetscVector<Number> X(x, sys.comm());
170 
171     // Perform a swap so that sys.solution points to the input vector
172     // "x", update sys.current_local_solution based on "x", then swap
173     // back.
174     X.swap(X_sys);
175     sys.update();
176     X.swap(X_sys);
177 
178     // Let's also wrap pc and h in PetscMatrix objects for convenience
179     PetscMatrix<Number> PC(pc, sys.comm());
180     PetscMatrix<Number> hessian(h, sys.comm());
181     PC.attach_dof_map(sys.get_dof_map());
182     hessian.attach_dof_map(sys.get_dof_map());
183 
184     // Enforce constraints exactly on the current_local_solution.
185     sys.get_dof_map().enforce_constraints_exactly(sys, sys.current_local_solution.get());
186 
187     if (solver->hessian_object != nullptr)
188       {
189         // Following PetscNonlinearSolver by passing in PC. It's not clear
190         // why we pass in PC and not hessian though?
191         solver->hessian_object->hessian(*(sys.current_local_solution), PC, sys);
192       }
193     else
194       libmesh_error_msg("Hessian function not defined in __libmesh_tao_hessian");
195 
196     PC.close();
197     hessian.close();
198 
199     return ierr;
200   }
201 
202 
203   //---------------------------------------------------------------
204   // This function is called by Tao to evaluate the equality constraints at x
205   PetscErrorCode
__libmesh_tao_equality_constraints(Tao,Vec x,Vec ce,void * ctx)206   __libmesh_tao_equality_constraints(Tao /*tao*/, Vec x, Vec ce, void * ctx)
207   {
208     LOG_SCOPE("equality_constraints()", "TaoOptimizationSolver");
209 
210     PetscErrorCode ierr = 0;
211 
212     libmesh_assert(x);
213     libmesh_assert(ce);
214     libmesh_assert(ctx);
215 
216     // ctx should be a pointer to the solver (it was passed in as void *)
217     TaoOptimizationSolver<Number> * solver =
218       static_cast<TaoOptimizationSolver<Number> *> (ctx);
219 
220     OptimizationSystem & sys = solver->system();
221 
222     // We'll use current_local_solution below, so let's ensure that it's consistent
223     // with the vector x that was passed in.
224     PetscVector<Number> & X_sys = *cast_ptr<PetscVector<Number> *>(sys.solution.get());
225     PetscVector<Number> X(x, sys.comm());
226 
227     // Perform a swap so that sys.solution points to the input vector
228     // "x", update sys.current_local_solution based on "x", then swap
229     // back.
230     X.swap(X_sys);
231     sys.update();
232     X.swap(X_sys);
233 
234     // We'll also pass the constraints vector ce into the assembly routine
235     // so let's make a PETSc vector for that too.
236     PetscVector<Number> eq_constraints(ce, sys.comm());
237 
238     // Clear the gradient prior to assembly
239     eq_constraints.zero();
240 
241     // Enforce constraints exactly on the current_local_solution.
242     sys.get_dof_map().enforce_constraints_exactly(sys, sys.current_local_solution.get());
243 
244     if (solver->equality_constraints_object != nullptr)
245       solver->equality_constraints_object->equality_constraints(*(sys.current_local_solution), eq_constraints, sys);
246     else
247       libmesh_error_msg("Constraints function not defined in __libmesh_tao_equality_constraints");
248 
249     eq_constraints.close();
250 
251     return ierr;
252   }
253 
254   //---------------------------------------------------------------
255   // This function is called by Tao to evaluate the Jacobian of the
256   // equality constraints at x
257   PetscErrorCode
__libmesh_tao_equality_constraints_jacobian(Tao,Vec x,Mat J,Mat Jpre,void * ctx)258   __libmesh_tao_equality_constraints_jacobian(Tao /*tao*/, Vec x, Mat J, Mat Jpre, void * ctx)
259   {
260     LOG_SCOPE("equality_constraints_jacobian()", "TaoOptimizationSolver");
261 
262     PetscErrorCode ierr = 0;
263 
264     libmesh_assert(x);
265     libmesh_assert(J);
266     libmesh_assert(Jpre);
267 
268     // ctx should be a pointer to the solver (it was passed in as void *)
269     TaoOptimizationSolver<Number> * solver =
270       static_cast<TaoOptimizationSolver<Number> *> (ctx);
271 
272     OptimizationSystem & sys = solver->system();
273 
274     // We'll use current_local_solution below, so let's ensure that it's consistent
275     // with the vector x that was passed in.
276     PetscVector<Number> & X_sys = *cast_ptr<PetscVector<Number> *>(sys.solution.get());
277     PetscVector<Number> X(x, sys.comm());
278 
279     // Perform a swap so that sys.solution points to the input vector
280     // "x", update sys.current_local_solution based on "x", then swap
281     // back.
282     X.swap(X_sys);
283     sys.update();
284     X.swap(X_sys);
285 
286     // Let's also wrap J and Jpre in PetscMatrix objects for convenience
287     PetscMatrix<Number> J_petsc(J, sys.comm());
288     PetscMatrix<Number> Jpre_petsc(Jpre, sys.comm());
289 
290     // Enforce constraints exactly on the current_local_solution.
291     sys.get_dof_map().enforce_constraints_exactly(sys, sys.current_local_solution.get());
292 
293     if (solver->equality_constraints_jacobian_object != nullptr)
294       solver->equality_constraints_jacobian_object->equality_constraints_jacobian(*(sys.current_local_solution), J_petsc, sys);
295     else
296       libmesh_error_msg("Constraints function not defined in __libmesh_tao_equality_constraints_jacobian");
297 
298     J_petsc.close();
299     Jpre_petsc.close();
300 
301     return ierr;
302   }
303 
304   //---------------------------------------------------------------
305   // This function is called by Tao to evaluate the inequality constraints at x
306   PetscErrorCode
__libmesh_tao_inequality_constraints(Tao,Vec x,Vec cineq,void * ctx)307   __libmesh_tao_inequality_constraints(Tao /*tao*/, Vec x, Vec cineq, void * ctx)
308   {
309     LOG_SCOPE("inequality_constraints()", "TaoOptimizationSolver");
310 
311     PetscErrorCode ierr = 0;
312 
313     libmesh_assert(x);
314     libmesh_assert(cineq);
315     libmesh_assert(ctx);
316 
317     // ctx should be a pointer to the solver (it was passed in as void *)
318     TaoOptimizationSolver<Number> * solver =
319       static_cast<TaoOptimizationSolver<Number> *> (ctx);
320 
321     OptimizationSystem & sys = solver->system();
322 
323     // We'll use current_local_solution below, so let's ensure that it's consistent
324     // with the vector x that was passed in.
325     PetscVector<Number> & X_sys = *cast_ptr<PetscVector<Number> *>(sys.solution.get());
326     PetscVector<Number> X(x, sys.comm());
327 
328     // Perform a swap so that sys.solution points to the input vector
329     // "x", update sys.current_local_solution based on "x", then swap
330     // back.
331     X.swap(X_sys);
332     sys.update();
333     X.swap(X_sys);
334 
335     // We'll also pass the constraints vector ce into the assembly routine
336     // so let's make a PETSc vector for that too.
337     PetscVector<Number> ineq_constraints(cineq, sys.comm());
338 
339     // Clear the gradient prior to assembly
340     ineq_constraints.zero();
341 
342     // Enforce constraints exactly on the current_local_solution.
343     sys.get_dof_map().enforce_constraints_exactly(sys, sys.current_local_solution.get());
344 
345     if (solver->inequality_constraints_object != nullptr)
346       solver->inequality_constraints_object->inequality_constraints(*(sys.current_local_solution), ineq_constraints, sys);
347     else
348       libmesh_error_msg("Constraints function not defined in __libmesh_tao_inequality_constraints");
349 
350     ineq_constraints.close();
351 
352     return ierr;
353   }
354 
355   //---------------------------------------------------------------
356   // This function is called by Tao to evaluate the Jacobian of the
357   // equality constraints at x
358   PetscErrorCode
__libmesh_tao_inequality_constraints_jacobian(Tao,Vec x,Mat J,Mat Jpre,void * ctx)359   __libmesh_tao_inequality_constraints_jacobian(Tao /*tao*/, Vec x, Mat J, Mat Jpre, void * ctx)
360   {
361     LOG_SCOPE("inequality_constraints_jacobian()", "TaoOptimizationSolver");
362 
363     PetscErrorCode ierr = 0;
364 
365     libmesh_assert(x);
366     libmesh_assert(J);
367     libmesh_assert(Jpre);
368 
369     // ctx should be a pointer to the solver (it was passed in as void *)
370     TaoOptimizationSolver<Number> * solver =
371       static_cast<TaoOptimizationSolver<Number> *> (ctx);
372 
373     OptimizationSystem & sys = solver->system();
374 
375     // We'll use current_local_solution below, so let's ensure that it's consistent
376     // with the vector x that was passed in.
377     PetscVector<Number> & X_sys = *cast_ptr<PetscVector<Number> *>(sys.solution.get());
378     PetscVector<Number> X(x, sys.comm());
379 
380     // Perform a swap so that sys.solution points to the input vector
381     // "x", update sys.current_local_solution based on "x", then swap
382     // back.
383     X.swap(X_sys);
384     sys.update();
385     X.swap(X_sys);
386 
387     // Let's also wrap J and Jpre in PetscMatrix objects for convenience
388     PetscMatrix<Number> J_petsc(J, sys.comm());
389     PetscMatrix<Number> Jpre_petsc(Jpre, sys.comm());
390 
391     // Enforce constraints exactly on the current_local_solution.
392     sys.get_dof_map().enforce_constraints_exactly(sys, sys.current_local_solution.get());
393 
394     if (solver->inequality_constraints_jacobian_object != nullptr)
395       solver->inequality_constraints_jacobian_object->inequality_constraints_jacobian(*(sys.current_local_solution), J_petsc, sys);
396     else
397       libmesh_error_msg("Constraints function not defined in __libmesh_tao_inequality_constraints_jacobian");
398 
399     J_petsc.close();
400     Jpre_petsc.close();
401 
402     return ierr;
403   }
404 
405 } // end extern "C"
406 //---------------------------------------------------------------------
407 
408 
409 
410 //---------------------------------------------------------------------
411 // TaoOptimizationSolver<> methods
412 template <typename T>
TaoOptimizationSolver(OptimizationSystem & system_in)413 TaoOptimizationSolver<T>::TaoOptimizationSolver (OptimizationSystem & system_in) :
414   OptimizationSolver<T>(system_in),
415   _reason(TAO_CONVERGED_USER) // Arbitrary initial value...
416 {
417 }
418 
419 
420 
421 template <typename T>
~TaoOptimizationSolver()422 TaoOptimizationSolver<T>::~TaoOptimizationSolver ()
423 {
424   this->clear ();
425 }
426 
427 
428 
429 template <typename T>
clear()430 void TaoOptimizationSolver<T>::clear ()
431 {
432   if (this->initialized())
433     {
434       this->_is_initialized = false;
435 
436       PetscErrorCode ierr=0;
437 
438       ierr = TaoDestroy(&_tao);
439       LIBMESH_CHKERR(ierr);
440     }
441 }
442 
443 
444 
445 template <typename T>
init()446 void TaoOptimizationSolver<T>::init ()
447 {
448   // Initialize the data structures if not done so already.
449   if (!this->initialized())
450     {
451       this->_is_initialized = true;
452 
453       PetscErrorCode ierr=0;
454 
455       ierr = TaoCreate(this->comm().get(),&_tao);
456       LIBMESH_CHKERR(ierr);
457     }
458 }
459 
460 template <typename T>
solve()461 void TaoOptimizationSolver<T>::solve ()
462 {
463   LOG_SCOPE("solve()", "TaoOptimizationSolver");
464 
465   this->init ();
466 
467   this->system().solution->zero();
468 
469   PetscMatrix<T> * hessian  = cast_ptr<PetscMatrix<T> *>(this->system().matrix);
470   // PetscVector<T> * gradient = cast_ptr<PetscVector<T> *>(this->system().rhs);
471   PetscVector<T> * x         = cast_ptr<PetscVector<T> *>(this->system().solution.get());
472   PetscVector<T> * ceq       = cast_ptr<PetscVector<T> *>(this->system().C_eq.get());
473   PetscMatrix<T> * ceq_jac   = cast_ptr<PetscMatrix<T> *>(this->system().C_eq_jac.get());
474   PetscVector<T> * cineq     = cast_ptr<PetscVector<T> *>(this->system().C_ineq.get());
475   PetscMatrix<T> * cineq_jac = cast_ptr<PetscMatrix<T> *>(this->system().C_ineq_jac.get());
476   PetscVector<T> * lb        = cast_ptr<PetscVector<T> *>(&this->system().get_vector("lower_bounds"));
477   PetscVector<T> * ub        = cast_ptr<PetscVector<T> *>(&this->system().get_vector("upper_bounds"));
478 
479   // Set the starting guess to zero.
480   x->zero();
481 
482   PetscErrorCode ierr = 0;
483 
484   // Workaround for bug where TaoSetFromOptions *reset*
485   // programmatically set tolerance and max. function evaluation
486   // values when "-tao_type ipm" was specified on the command line: we
487   // call TaoSetFromOptions twice (both before and after setting
488   // custom options programmatically)
489   ierr = TaoSetFromOptions(_tao);
490   LIBMESH_CHKERR(ierr);
491 
492   // Set convergence tolerances
493   // f(X) - f(X*) (estimated)            <= fatol
494   // |f(X) - f(X*)| (estimated) / |f(X)| <= frtol
495   // ||g(X)||                            <= gatol
496   // ||g(X)|| / |f(X)|                   <= grtol
497   // ||g(X)|| / ||g(X0)||                <= gttol
498   // Command line equivalents: -tao_fatol, -tao_frtol, -tao_gatol, -tao_grtol, -tao_gttol
499   ierr = TaoSetTolerances(_tao,
500 #if PETSC_RELEASE_LESS_THAN(3,7,0)
501                           // Releases up to 3.X.Y had fatol and frtol, after that they were removed.
502                           // Hopefully we'll be able to know X and Y soon. Guessing at 3.7.0.
503                           /*fatol=*/PETSC_DEFAULT,
504                           /*frtol=*/PETSC_DEFAULT,
505 #endif
506                           /*gatol=*/PETSC_DEFAULT,
507                           /*grtol=*/this->objective_function_relative_tolerance,
508                           /*gttol=*/PETSC_DEFAULT);
509   LIBMESH_CHKERR(ierr);
510 
511   // Set the max-allowed number of objective function evaluations
512   // Command line equivalent: -tao_max_funcs
513   ierr = TaoSetMaximumFunctionEvaluations(_tao, this->max_objective_function_evaluations);
514   LIBMESH_CHKERR(ierr);
515 
516   // Set the max-allowed number of optimization iterations.
517   // Command line equivalent: -tao_max_it
518   // Not implemented for now as it seems fairly similar to
519   // ierr = TaoSetMaximumIterations(_tao, 4);
520   // LIBMESH_CHKERR(ierr);
521 
522   // Set solution vec and an initial guess
523   ierr = TaoSetInitialVector(_tao, x->vec());
524   LIBMESH_CHKERR(ierr);
525 
526   // We have to have an objective function
527   libmesh_assert( this->objective_object );
528 
529   // Set routines for objective, gradient, hessian evaluation
530   ierr = TaoSetObjectiveRoutine(_tao, __libmesh_tao_objective, this);
531   LIBMESH_CHKERR(ierr);
532 
533   if (this->gradient_object)
534     {
535       ierr = TaoSetGradientRoutine(_tao, __libmesh_tao_gradient, this);
536       LIBMESH_CHKERR(ierr);
537     }
538 
539   if (this->hessian_object)
540     {
541       ierr = TaoSetHessianRoutine(_tao, hessian->mat(), hessian->mat(), __libmesh_tao_hessian, this);
542       LIBMESH_CHKERR(ierr);
543     }
544 
545   if (this->lower_and_upper_bounds_object)
546     {
547       // Need to actually compute the bounds vectors first
548       this->lower_and_upper_bounds_object->lower_and_upper_bounds(this->system());
549 
550       ierr = TaoSetVariableBounds(_tao,
551                                   lb->vec(),
552                                   ub->vec());
553       LIBMESH_CHKERR(ierr);
554     }
555 
556   if (this->equality_constraints_object)
557     {
558       ierr = TaoSetEqualityConstraintsRoutine(_tao, ceq->vec(), __libmesh_tao_equality_constraints, this);
559       LIBMESH_CHKERR(ierr);
560     }
561 
562   if (this->equality_constraints_jacobian_object)
563     {
564       ierr = TaoSetJacobianEqualityRoutine(_tao,
565                                            ceq_jac->mat(),
566                                            ceq_jac->mat(),
567                                            __libmesh_tao_equality_constraints_jacobian,
568                                            this);
569       LIBMESH_CHKERR(ierr);
570     }
571 
572   // Optionally set inequality constraints
573   if (this->inequality_constraints_object)
574     {
575       ierr = TaoSetInequalityConstraintsRoutine(_tao, cineq->vec(), __libmesh_tao_inequality_constraints, this);
576       LIBMESH_CHKERR(ierr);
577     }
578 
579   // Optionally set inequality constraints Jacobian
580   if (this->inequality_constraints_jacobian_object)
581     {
582       ierr = TaoSetJacobianInequalityRoutine(_tao,
583                                              cineq_jac->mat(),
584                                              cineq_jac->mat(),
585                                              __libmesh_tao_inequality_constraints_jacobian,
586                                              this);
587       LIBMESH_CHKERR(ierr);
588     }
589 
590   // Check for Tao command line options
591   ierr = TaoSetFromOptions(_tao);
592   LIBMESH_CHKERR(ierr);
593 
594   // Perform the optimization
595   ierr = TaoSolve(_tao);
596   LIBMESH_CHKERR(ierr);
597 
598   // Enforce constraints exactly now that the solve is done.  We have
599   // been enforcing them on the current_local_solution during the
600   // solve, but now need to be sure they are enforced on the parallel
601   // solution vector as well.
602   this->system().get_dof_map().enforce_constraints_exactly(this->system());
603 
604   // Store the convergence/divergence reason
605   ierr = TaoGetConvergedReason(_tao, &_reason);
606   LIBMESH_CHKERR(ierr);
607 }
608 
609 
610 template <typename T>
get_dual_variables()611 void TaoOptimizationSolver<T>::get_dual_variables()
612 {
613   LOG_SCOPE("get_dual_variables()", "TaoOptimizationSolver");
614 
615   PetscVector<T> * lambda_eq_petsc =
616     cast_ptr<PetscVector<T> *>(this->system().lambda_eq.get());
617   PetscVector<T> * lambda_ineq_petsc =
618     cast_ptr<PetscVector<T> *>(this->system().lambda_ineq.get());
619 
620   Vec lambda_eq_petsc_vec = lambda_eq_petsc->vec();
621   Vec lambda_ineq_petsc_vec = lambda_ineq_petsc->vec();
622 
623   PetscErrorCode ierr = 0;
624   ierr = TaoGetDualVariables(_tao,
625                              &lambda_eq_petsc_vec,
626                              &lambda_ineq_petsc_vec);
627   LIBMESH_CHKERR(ierr);
628 }
629 
630 
631 template <typename T>
print_converged_reason()632 void TaoOptimizationSolver<T>::print_converged_reason()
633 {
634   libMesh::out << "Tao optimization solver convergence/divergence reason: "
635                << TaoConvergedReasons[this->get_converged_reason()] << std::endl;
636 }
637 
638 
639 
640 template <typename T>
get_converged_reason()641 int TaoOptimizationSolver<T>::get_converged_reason()
642 {
643   PetscErrorCode ierr=0;
644 
645   if (this->initialized())
646     {
647       ierr = TaoGetConvergedReason(_tao, &_reason);
648       LIBMESH_CHKERR(ierr);
649     }
650 
651   return static_cast<int>(_reason);
652 }
653 
654 
655 //------------------------------------------------------------------
656 // Explicit instantiations
657 template class TaoOptimizationSolver<Number>;
658 
659 } // namespace libMesh
660 
661 
662 
663 #endif // #if defined(LIBMESH_HAVE_PETSC_TAO) && !defined(LIBMESH_USE_COMPLEX_NUMBERS)
664